home *** CD-ROM | disk | FTP | other *** search
/ System Booster / System Booster.iso / Archives / GNU / GNUPLOTsrc.lha / graphics.c < prev    next >
C/C++ Source or Header  |  1996-01-22  |  102KB  |  3,631 lines

  1. #ifndef lint
  2. static char *RCSid = "$Id: graphics.c,v 1.93 1995/12/20 22:39:34 drd Exp $";
  3. #endif
  4.  
  5.  
  6. /* GNUPLOT - graphics.c */
  7. /*
  8.  * Copyright (C) 1986 - 1993   Thomas Williams, Colin Kelley
  9.  *
  10.  * Permission to use, copy, and distribute this software and its
  11.  * documentation for any purpose with or without fee is hereby granted, 
  12.  * provided that the above copyright notice appear in all copies and 
  13.  * that both that copyright notice and this permission notice appear 
  14.  * in supporting documentation.
  15.  *
  16.  * Permission to modify the software is granted, but not the right to
  17.  * distribute the modified code.  Modifications are to be distributed 
  18.  * as patches to released version.
  19.  *  
  20.  * This software is provided "as is" without express or implied warranty.
  21.  * 
  22.  *
  23.  * AUTHORS
  24.  * 
  25.  *   Original Software:
  26.  *     Thomas Williams,  Colin Kelley.
  27.  * 
  28.  *   Gnuplot 2.0 additions:
  29.  *       Russell Lang, Dave Kotz, John Campbell.
  30.  *
  31.  *   Gnuplot 3.0 additions:
  32.  *       Gershon Elber and many others.
  33.  *
  34.  * There is a mailing list for gnuplot users. Note, however, that the
  35.  * newsgroup 
  36.  *    comp.graphics.gnuplot 
  37.  * is identical to the mailing list (they
  38.  * both carry the same set of messages). We prefer that you read the
  39.  * messages through that newsgroup, to subscribing to the mailing list.
  40.  * (If you can read that newsgroup, and are already on the mailing list,
  41.  * please send a message info-gnuplot-request@dartmouth.edu, asking to be
  42.  * removed from the mailing list.)
  43.  *
  44.  * The address for mailing to list members is
  45.  *       info-gnuplot@dartmouth.edu
  46.  * and for mailing administrative requests is 
  47.  *       info-gnuplot-request@dartmouth.edu
  48.  * The mailing list for bug reports is 
  49.  *       bug-gnuplot@dartmouth.edu
  50.  * The list of those interested in beta-test versions is
  51.  *       info-gnuplot-beta@dartmouth.edu
  52.  */
  53.  
  54. #include <math.h>
  55. #include <assert.h>
  56. #include <ctype.h>
  57. #include "plot.h"
  58. #include "setshow.h"
  59.  
  60. /* key placement is calculated in boundary, so we need file-wide variables
  61.  * To simplify adjustments to the key, we set all these once [depends on
  62.  * key_reverse] and use them throughout.
  63.  */
  64.  
  65. /*{{{  local and global variables*/
  66. static int key_sample_left;   /* offset from x for left of line sample */
  67. static int key_sample_right;  /* offset from x for right of line sample */
  68. static int key_point_offset;  /* offset from x for point sample */
  69. static int key_text_left;     /* offset from x for left-justified text */
  70. static int key_text_right;    /* offset from x for right-justified text */
  71. static int key_size_left;     /* size of left bit of key (text or sample, depends on key_reverse) */
  72. static int key_size_right;    /* size of right part of key (including padding) */
  73.  
  74. /* I think the following should also be static ?? */
  75.  
  76. static int key_xl, key_xr, key_yt, key_yb;  /* boundarys for key field */
  77. static int max_ptitl_len = 0;    /* max length of plot-titles (keys) */
  78. static int ktitl_lines = 0;    /* no lines in key_title (key header) */
  79. static int ptitl_cnt;    /* count keys with len > 0  */
  80. static int key_cols; /* no cols of keys */
  81. static int key_rows, key_col_wth, yl_ref;
  82.  
  83.  
  84. /* penalty for doing tics by callback in gen_tics is need for
  85.  * global variables to communicate with the tic routines
  86.  * Dont need to be arrays for this
  87.  */
  88. static int tic_start, tic_direction, tic_text, tic_just, tic_mirror;
  89.  
  90. /* set by tic_callback - how large to draw polar radii */
  91. static double largest_polar_circle;
  92.  
  93. /* either xformat etc or invented time format
  94.  * index with FIRST_X_AXIS etc
  95.  * global because used in gen_tics, which graph3d also uses
  96.  */
  97. char ticfmt[8][25];
  98. int timelevel[8];
  99. double ticstep[8];
  100.  
  101. static int xlablin, x2lablin, ylablin, y2lablin, titlelin, xticlin, x2ticlin;
  102. static unsigned int x2ticdelta; /* 0 for tic_in or no x2tics, ticscale*v_tic otherwise */
  103. static int top_margin; /* in lines, excluding x2tics */
  104.  
  105. static int key_entry_height;  /* bigger of t->v_size, pointsize*t->v_tick */
  106. static int p_width, p_height;  /* pointsize * { t->h_tic | t->v_tic } */
  107.  
  108. #if defined(DJGPP)||defined(sun386)
  109. #define time_t unsigned long
  110. #endif
  111.  
  112. /* there are several things on right of plot - key, y2tics and y2label
  113.  * when working out boundary, save posn of y2label for later...
  114.  * Same goes for x2label.
  115.  * key posn is also stored in key_xl, and tics go at xright
  116.  */
  117. static int y2label_x;
  118. /*}}}*/
  119.  
  120. /*{{{  static fns and local macros*/
  121. static void plot_impulses __P((struct curve_points *plot, int yaxis_x, int xaxis_y));
  122. static void plot_lines __P((struct curve_points *plot));
  123. static void plot_points __P((struct curve_points *plot));
  124. static void plot_dots __P((struct curve_points *plot));
  125. static void plot_bars __P((struct curve_points *plot));
  126. static void plot_boxes __P((struct curve_points *plot, int xaxis_y));
  127. static void plot_vectors __P((struct curve_points *plot));
  128. static void edge_intersect __P((struct coordinate GPHUGE *points, int i, double *ex, double *ey));
  129. static int two_edge_intersect __P((struct coordinate GPHUGE *points, int i, double *lx, double *ly));
  130. static TBOOLEAN two_edge_intersect_steps __P((struct coordinate GPHUGE *points, int i, double *lx, double *ly));
  131.  
  132. static void plot_steps __P((struct curve_points *plot));    /* JG */
  133. static void plot_fsteps __P((struct curve_points *plot));    /* HOE */
  134. static void edge_intersect_steps __P((struct coordinate GPHUGE *points, int i, double *ex, double *ey));        /* JG */
  135. static void edge_intersect_fsteps __P((struct coordinate GPHUGE *points, int i, double *ex, double *ey));         /* HOE */
  136. static TBOOLEAN two_edge_intersect_steps __P((struct coordinate GPHUGE *points, int i, double *lx, double *ly));    /* JG */
  137. static TBOOLEAN two_edge_intersect_fsteps __P((struct coordinate GPHUGE *points, int i, double *lx, double *ly));
  138.  
  139. static double LogScale __P((double coord, int is_log, double log_base_log, char *what, char *axis));
  140. static double dbl_raise __P((double x, int y));
  141. static void boundary __P((int scaling, struct curve_points *plots, int count));
  142. static double set_tic __P((double l10, int guide));
  143. static double make_tics __P((int axis, int guide));
  144. static int widest_tic;  /* widest2d_callback keeps longest so far in here */
  145. static void widest2d_callback __P((int axis, double place, char *text, int grid));
  146. static void ytick2d_callback __P((int axis, double place, char *text, int grid));
  147. static void xtick2d_callback __P((int axis, double place, char *text, int grid));
  148. static void map_position __P((struct position *pos, unsigned int *x, unsigned int *y, char *what));
  149.  
  150. #if defined(sun386) || defined(AMIGA_SC_6_1)
  151. static double CheckLog __P((TBOOLEAN is_log, double base_log, double x));
  152. #endif
  153.  
  154. /* for plotting error bars */
  155. #define ERRORBARTIC (t->h_tic/2) /* half the width of error bar tic mark */
  156.  
  157. #ifndef max        /* Lattice C has max() in math.h, but shouldn't! */
  158. #define max(a,b) ((a > b) ? a : b)
  159. #endif
  160.  
  161. #ifndef min
  162. #define min(a,b) ((a < b) ? a : b)
  163. #endif
  164.  
  165. /*
  166.  * The Amiga SAS/C 6.2 compiler moans about macro envocations causing
  167.  * multiple calls to functions. I converted these macros to inline
  168.  * functions coping with the problem without loosing speed.
  169.  * If your compiler supports __inline, you should add it to the
  170.  * #ifdef directive
  171.  * (MGR, 1993)
  172.  */
  173.  
  174. #ifdef AMIGA_SC_6_1
  175. __inline static TBOOLEAN i_inrange(int z,int min,int max)
  176. {
  177.   return((min<max) ? ((z>=min)&&(z<=max)) : ((z>=max)&&(z<=min)));
  178. }
  179.  
  180. __inline static double f_max(double a,double b)
  181. {
  182.   return(max(a,b));
  183. }
  184.  
  185. __inline static double f_min(double a,double b)
  186. {
  187.   return(min(a,b));
  188. }
  189.  
  190. #else
  191. #define f_max(a,b) max((a),(b))
  192. #define f_min(a,b) min((a),(b))
  193. #define i_inrange(z,a,b) inrange((z),(a),(b))
  194. #endif
  195.  
  196. #define inrange(z,min,max) ((min<max) ? ((z>=min)&&(z<=max)) : ((z>=max)&&(z<=min)) )
  197.  
  198. /* True if a and b have the same sign or zero (positive or negative) */
  199. #define samesign(a,b) ((a) * (b) >= 0)
  200. /*}}}*/
  201.  
  202. /*{{{  more variables*/
  203. /* Define the boundary of the plot
  204.  * These are computed at each call to do_plot, and are constant over
  205.  * the period of one do_plot. They actually only change when the term
  206.  * type changes and when the 'set size' factors change.
  207.  * - no longer true, for 'set key out' or 'set key under'. also depend
  208.  * on tic marks and multi-line labels.
  209.  * They are shared with graph3d.c since we want to use its draw_clip_line()
  210.  */
  211. int xleft, xright, ybot, ytop;
  212.  
  213.  
  214. /* we make a local copy of the 'key' variable so that if something
  215.  * goes wrong, we can switch it off temporarily
  216.  */
  217.  
  218. static int lkey;
  219.  
  220. /* First attempt at double axes...
  221.  * x_min etc are now accessed from a global array min_array[], max_array[]
  222.  * put the scale factors into a similar array
  223.  * for convenience in this first attack on double axes, just define x_min etc
  224.  * since code already uses x_min, etc  Eventually it will be done properly
  225.  */
  226.  
  227. extern TBOOLEAN term_graphics, term_suspended;
  228.  
  229. extern double min_array[], max_array[];
  230. extern int auto_array[];
  231.  
  232. extern int log_array[];
  233. extern double base_array[], log_base_array[];
  234.  
  235. static int x_axis=FIRST_X_AXIS, y_axis=FIRST_Y_AXIS;  /* current axes */
  236.  
  237. static double scale[AXIS_ARRAY_SIZE];  /* scale factors for mapping for each axis */
  238.  
  239. /* BODGES BEFORE I FIX IT UP */
  240. #define x_min min_array[x_axis]
  241. #define x_max max_array[x_axis]
  242. #define y_min min_array[y_axis]
  243. #define y_max max_array[y_axis]
  244.  
  245. /* And the functions to map from user to terminal coordinates */
  246. #define map_x(x) (int)(xleft+(x-min_array[x_axis])*scale[x_axis]+0.5) /* maps floating point x to screen */ 
  247. #define map_y(y) (int)(ybot +(y-min_array[y_axis])*scale[y_axis]+0.5)    /* same for y */
  248.  
  249. /* (DFK) Watch for cancellation error near zero on axes labels */
  250. #define SIGNIF (0.01)        /* less than one hundredth of a tic mark */
  251. #define CheckZero(x,tic) (fabs(x) < ((tic) * SIGNIF) ? 0.0 : (x))
  252. #define NearlyEqual(x,y,tic) (fabs((x)-(y)) < ((tic) * SIGNIF))
  253. /*}}}*/
  254.  
  255. /*{{{  CheckLog()*/
  256. /* (DFK) For some reason, the Sun386i compiler screws up with the CheckLog 
  257.  * macro, so I write it as a function on that machine.
  258.  *
  259.  * Amiga SAS/C 6.2 thinks it will do too much work calling functions in
  260.  * macro arguments twice, thus I inline theese functions. (MGR, 1993)
  261.  * If your compiler doesn't handle those macros correctly, you should
  262.  * also subscribe here. Even without inlining you gain speed with log plots
  263.  */
  264. #if defined(sun386) || defined(AMIGA_SC_6_1)
  265. #ifdef AMIGA_SC_6_1
  266. __inline
  267. #endif
  268. static double
  269. CheckLog(is_log, base_log, x)
  270.      TBOOLEAN is_log;
  271.      double base_log;
  272.      double x;
  273. {
  274.   if (is_log)
  275.     return(pow(base_log, x));
  276.   else
  277.     return(x);
  278. }
  279. #else
  280. /* (DFK) Use 10^x if logscale is in effect, else x */
  281. #define CheckLog(is_log, base_log, x) ((is_log) ? pow(base_log, (x)) : (x))
  282. #endif /* sun386 || SAS/C */
  283. /*}}}*/
  284.  
  285. /*{{{  LogScale()*/
  286. static double
  287. LogScale(coord, is_log, log_base_log, what, axis)
  288.     double coord;            /* the value */
  289.     TBOOLEAN is_log;            /* is this axis in logscale? */
  290.         double log_base_log;        /* if so, the log of its base */
  291.     char *what;            /* what is the coord for? */
  292.     char *axis;            /* which axis is this for ("x" or "y")? */
  293. {
  294.     if (is_log) {
  295.        if (coord <= 0.0) {
  296.           char errbuf[100];        /* place to write error message */
  297.         (void) sprintf(errbuf,"%s has %s coord of %g; must be above 0 for log scale!",
  298.                 what, axis, coord);
  299.           graph_error(errbuf);
  300.        } else
  301.         return(log(coord)/log_base_log);
  302.     }
  303.     return(coord);
  304. }
  305. /*}}}*/
  306.  
  307. /*{{{  graph_error()*/
  308. /* handle errors during graph-plot in a consistent way */
  309. void graph_error(text)
  310. char *text;
  311. {
  312.     if (term_graphics) {
  313.         if (multiplot && term_suspended && term->resume)
  314.             (*term->resume)(); /* some drivers may rely on this ? */
  315.         (term->text)();
  316.         fflush(outfile);
  317.         term_graphics = FALSE;
  318.         term_suspended= FALSE;
  319.         multiplot = FALSE;
  320.     }
  321.     int_error(text, NO_CARET);
  322. }
  323. /*}}}*/
  324.  
  325. /*{{{  widest2d_callback()*/
  326. /* we determine widest tick label by getting gen_ticks to call this
  327.  * routine with every label
  328.  */
  329.  
  330. static void widest2d_callback(axis, place, text, grid)
  331. int axis;
  332. double place;
  333. char *text;
  334. int grid;
  335. {
  336.     int len=label_width(text, NULL);
  337.     if (len>widest_tic)
  338.         widest_tic = len;
  339. }
  340. /*}}}*/
  341.  
  342.  
  343. /*{{{  boundary()*/
  344. /* borders of plotting area
  345.  * computed once on every call to do_plot
  346.  *
  347.  * The order in which things is done is getting pretty critical:
  348.  *  ytop depends on title, x2label, ylabels (if no rotated text)
  349.  *  ybot depends on key, if TUNDER
  350.  *  once we have these, we can setup the y1 and y2 tics and the 
  351.  *  only then can we calculate xleft and xright
  352.  *  xright depends also on key TRIGHT
  353.  *  then we can do x and x2 tics
  354.  *
  355.  * For set size square, everything depends on everything else...
  356.  * not really a lot we can do about that, so we lose if the plot has to
  357.  * be reduced vertically. But the chances are the
  358.  * change will not be very big, so the number of tics will not
  359.  * change dramatically.
  360.  */
  361.  
  362. static void boundary(scaling,plots,count)
  363.     TBOOLEAN scaling;    /* TRUE if terminal is doing the scaling */
  364.     struct curve_points *plots;
  365.     int count;
  366. {
  367.     int ytlen;
  368.     register struct termentry *t = term;
  369.     int key_h, key_w;
  370.     int can_rotate=(*t->text_angle)(1);
  371.  
  372.     int yticlin=0, y2ticlin=0, timelin=0;
  373.  
  374.     lkey = key; /* but we may have to disable it later */
  375.  
  376.    xticlin = ylablin = y2lablin = xlablin = x2lablin = titlelin = top_margin = 0;
  377.  
  378.    /*{{{  count lines in labels and tics*/
  379.    if (*title.text) label_width(title.text, &titlelin);
  380.    if (*xlabel.text) label_width(xlabel.text, &xlablin);
  381.    if (*x2label.text) label_width(x2label.text, &x2lablin);
  382.    if (*ylabel.text) label_width(ylabel.text, &ylablin);
  383.    if (*y2label.text) label_width(y2label.text, &y2lablin);
  384.    if (xtics) label_width(xformat, &xticlin);
  385.    if (x2tics) label_width(x2format, &x2ticlin);
  386.    if (ytics) label_width(yformat, &yticlin);
  387.    if (y2tics) label_width(y2format, &y2ticlin);
  388.    if (*timelabel.text) label_width(timelabel.text, &timelin);
  389.    /*}}}*/
  390.  
  391.     /*{{{  ytop*/
  392.         
  393.     if (tmargin >= 0)
  394.         top_margin = tmargin;
  395.     else {
  396.         /* allow an extra one if both are present */
  397.         top_margin = titlelin + x2lablin + (titlelin && x2lablin);
  398.         
  399.         if (*ylabel.text && !can_rotate && ylablin>top_margin)
  400.             top_margin = ylablin;
  401.         
  402.         if (*y2label.text && !can_rotate && y2lablin>top_margin)
  403.             top_margin = y2lablin;
  404.     }
  405.     
  406.     if (x2tics&TICS_ON_BORDER) {
  407.         /* ought to consider tics on axes if axis near border */
  408.         top_margin += x2ticlin+0.5;
  409.     }
  410.     
  411.     if (tmargin < 0 && !tic_in && (x2tics&TICS_ON_BORDER || xtics&TICS_MIRROR))
  412.         x2ticdelta = (t->v_tic)*ticscale;
  413.     else
  414.         x2ticdelta = 0;
  415.     
  416.     ytop = (ysize+yoffset) * (t->ymax) - (t->v_char)*(top_margin+1) - x2ticdelta;
  417.     /*}}}*/
  418.  
  419.     /*{{{  tentative xleft, needed for TUNDER*/
  420.     xleft = xoffset * (t->xmax) + (lmargin > 0 ? (t->h_char)*lmargin : t->h_tic);
  421.     /*}}}*/
  422.     
  423.     /*{{{  tentative xright, needed for TUNDER*/
  424.     if (rmargin >= 0)
  425.         xright = (xsize+xoffset) * (t->xmax) - (t->h_char)*rmargin;
  426.     else
  427.         xright = (xsize+xoffset) * (t->xmax) - (t->h_char)*2 - (t->h_tic);
  428.     /*}}}*/
  429.     
  430.     /*{{{  ybot*/
  431.     ybot = t->ymax * yoffset;
  432.     
  433.     if (bmargin >= 0)
  434.         ybot += bmargin * (t->v_char);
  435.     else {
  436.         if (xtics&TICS_ON_BORDER)
  437.             ybot += (t->v_char)*(xticlin+0.5);
  438.         else
  439.             ybot += (t->v_char)*0.5;
  440.     
  441.         if (*xlabel.text || (!can_rotate && *timelabel.text))
  442.         {
  443.             ybot += (t->v_char)*(((!can_rotate && (timelin > xlablin)) ? timelin : xlablin)+0.5);
  444.         }
  445.         
  446.         if (!tic_in && (xtics&TICS_ON_BORDER || x2tics&TICS_MIRROR))
  447.             ybot += (t->v_tic)*ticscale;
  448.     
  449.     }
  450.     /*}}}*/
  451.  
  452. #define KEY_PANIC(x) if (x) { lkey=0; goto key_escape; }
  453.  
  454.     if (lkey) {
  455.         /*{{{  essential key features*/
  456.         p_width  = pointsize * t->h_tic;
  457.         p_height = pointsize * t->v_tic;
  458.         
  459.         key_entry_height = p_height * 1.25;
  460.         if (key_entry_height < (t->v_char) )
  461.             key_entry_height = (t->v_char);
  462.         
  463.         /* count max_len key and number keys with len > 0 */
  464.         max_ptitl_len = find_maxl_keys(plots,count,&ptitl_cnt);
  465.         if ( (ytlen = label_width(key_title,&ktitl_lines)) > max_ptitl_len ) 
  466.             max_ptitl_len = ytlen;
  467.         
  468.         if (key_reverse) {
  469.             key_sample_left= -4*(t->h_char) - p_width;
  470.             key_sample_right= 0;
  471.             key_text_left=t->h_char;
  472.             key_text_right=(t->h_char)*(max_ptitl_len+1);
  473.             key_size_right=key_text_right + t->h_char;
  474.             key_size_left=t->h_char - key_sample_left;  /* sample left is -ve */
  475.             key_size_right=key_text_right + t->h_char;
  476.         } else {
  477.             key_sample_left= 0;
  478.             key_sample_right= 4*(t->h_char) + p_width;
  479.             key_text_left=-(t->h_char)*(max_ptitl_len+1);
  480.             key_text_right=-(t->h_char);
  481.             key_size_left=t->h_char - key_text_left;
  482.             key_size_right=key_sample_right + t->h_char;
  483.         }
  484.         key_point_offset = (key_sample_left + key_sample_right)/2;
  485.         
  486.         /* advance width for cols */
  487.         key_col_wth = key_size_left + key_size_right;
  488.         
  489.         key_rows = ptitl_cnt;
  490.         key_cols = 1;
  491.         
  492.         /* calculate rows and cols for key - if something goes wrong,
  493.          * the tidiest way out is to  set lkey=0, and a goto
  494.          */
  495.          
  496.         if (lkey == -1) {
  497.             if (key_vpos == TUNDER ) {
  498.                 /* maximise no cols, limited by label-length */
  499.                 key_cols = (int)(xright - xleft)/key_col_wth;
  500.                 KEY_PANIC(key_cols == 0);
  501.                 key_rows = (int)(ptitl_cnt+key_cols-1)/key_cols;
  502.                 KEY_PANIC(key_rows==0);
  503.                 /* now calculate actual no cols depending on no rows */
  504.                 key_cols = (int)(ptitl_cnt+key_rows-1)/key_rows;
  505.                 KEY_PANIC(key_cols==0);
  506.                 key_col_wth = (int)(xright - xleft)/key_cols;
  507.                  /* we divide into columns, then centre in column by considering ratio
  508.                   * of key_left_size to key_right_size
  509.                  * key_size_left/(key_size_left+key_size_right) * (xright-xleft)/key_cols
  510.                  * do one integer division to maximise accuracy (hope we dont overflow !)
  511.                  */
  512.                 key_xl = xleft - key_size_left + ((xright-xleft)*key_size_left) / (key_cols*(key_size_left+key_size_right));
  513.                 key_xr = key_xl + key_col_wth * (key_cols-1) + key_size_left + key_size_right;
  514.                 key_yb = t->ymax * yoffset;
  515.                 key_yt = key_yb + key_rows * key_entry_height + ktitl_lines * t->v_char;
  516.                 ybot += key_entry_height*key_rows + t->v_char*(ktitl_lines+1);
  517.             } else {
  518.                 /* maximise no rows, limited by ytop-ybot */
  519.                 int i = (int)(ytop-ybot-(ktitl_lines+1)*(t->v_char))/key_entry_height;
  520.                 KEY_PANIC(i==0);
  521.                 if (ptitl_cnt > i) {
  522.                     key_cols = (int)(ptitl_cnt+i-1)/i;
  523.                     /* now calculate actual no rows depending on no cols */
  524.                     KEY_PANIC(key_cols==0);
  525.                     key_rows = (int)(ptitl_cnt+key_cols-1)/key_cols;
  526.                 }
  527.             }
  528.             /* come here if we detect a division by zero in key calculations */
  529.             key_escape:
  530.                 ; /* ansi requires this */
  531.         }
  532.         /*}}}*/
  533.     }
  534.  
  535.  
  536.     /*{{{  set up y and y2 tics*/
  537.     {
  538.         int guide = (ytop-ybot)/term->v_char;
  539.         if (ytics) setup_tics(FIRST_Y_AXIS, &yticdef, yformat, (int) (guide/yticlin));
  540.         if (y2tics) setup_tics(SECOND_Y_AXIS, &y2ticdef, y2format, (int) (guide/y2ticlin));
  541.     }
  542.     /*}}}*/
  543.     
  544.     /*{{{  fix up xleft based on widest ytic, ylabels, etc*/
  545.     if ( (lmargin == -1)) {
  546.         if (ytics&TICS_ON_BORDER) {
  547.             widest_tic=0; /* reset the global variable ... */
  548.             /* get gen_tics to call widest2d_callback with all labels
  549.              * the latter sets widest_tic to the length of the widest one
  550.              * ought to consider tics on axis if axis near border...
  551.              */
  552.             gen_tics(FIRST_Y_AXIS, &yticdef, 0, 0, 0.0, widest2d_callback);
  553.     
  554.             xleft += (t->h_char)*(widest_tic+3);
  555.         }
  556.     
  557.         if (*ylabel.text || (can_rotate && *timelabel.text)) {
  558.             xleft += (t->v_char)*(can_rotate && timelin>ylablin ? timelin : ylablin);
  559.         }
  560.     
  561.             if (!tic_in && (ytics&TICS_ON_BORDER || y2tics&TICS_MIRROR))
  562.                 xleft += (t->h_tic)*ticscale;
  563.     }
  564.     /*}}}*/
  565.     
  566.     /*{{{  fix up xright based on widest y2tic. y2labels, key TOUT*/
  567.     if ( rmargin<0 && lkey == -1 && key_hpos == TOUT ) {
  568.         xright -= key_col_wth*key_cols; 
  569.         key_xl = xright  + (t->h_tic);
  570.     }
  571.     
  572.     /* fix up xright some more if y2label
  573.      * also, store y2label posn, since it is not trivial to
  574.      * work out again
  575.      */
  576.     
  577.     if ( *y2label.text) {
  578.         if (can_rotate && rmargin<0)
  579.             xright -= (t->v_char)*y2lablin;
  580.         /* else it goes at top, so we dont need to move xright */
  581.         
  582.         y2label_x = xright; /* right or top justified to here */
  583.     }
  584.     
  585.     /* fix up xright yet more if second tics */
  586.     if (rmargin<0) {
  587.         if (y2tics&TICS_ON_BORDER) {
  588.             /* dont use map_x/map_y, so dont need to set x_axis/y_axis */
  589.             widest_tic=0; /* reset the global variable */
  590.             gen_tics(SECOND_Y_AXIS, &y2ticdef, 0, 0, 0.0, widest2d_callback);
  591.             xright -= (widest_tic+1) * (t->h_char);
  592.         }
  593.     
  594.         if (!tic_in && (y2tics&TICS_ON_BORDER || ytics&TICS_MIRROR))
  595.             xright -= (t->h_tic)*ticscale;
  596.      }
  597.     /*}}}*/
  598.     
  599.    /*{{{  set aspect ratio if required*/
  600.    if (square) {
  601.        double current = ( (double) (ytop-ybot)) / ( (double) (xright-xleft) );
  602.        double required= ( (double) t->v_tic) / ( (double) t->h_tic);
  603.    
  604.        if (current > required) {
  605.            /* too tall */
  606.            ytop = ybot + required * (xright-xleft);
  607.        } else {
  608.             int y2 = y2label_x - xright;
  609.            xright = xleft + (ytop-ybot)/required;
  610.            y2label_x = xright + y2;
  611.        }
  612.    }
  613.    /*}}}*/
  614.  
  615.     /*{{{  set up x and x2 tics*/
  616.     /* we should base the guide on the width of the xtics, but we cannot
  617.      * use widest_tics until tics are set up. Bit of a downer - let us
  618.      * assume tics are 5 characters wide
  619.      */
  620.     
  621.      {
  622.          int guide = (xright - xleft) / (5*t->h_char);
  623.     
  624.         if (xtics) setup_tics(FIRST_X_AXIS, &xticdef, xformat, guide);
  625.         if (x2tics) setup_tics(SECOND_X_AXIS, &x2ticdef, x2format, guide);
  626.      }
  627.     /*}}}*/
  628.  
  629.     /* restore text to horizontal [we tested rotation above] */
  630.     (void)(*t->text_angle)(0);
  631.  
  632.     /* needed for map_position() below */
  633.     
  634.     scale[FIRST_Y_AXIS] = (ytop - ybot)/(max_array[FIRST_Y_AXIS] - min_array[FIRST_Y_AXIS]);
  635.     scale[FIRST_X_AXIS] = (xright - xleft)/(max_array[FIRST_X_AXIS] - min_array[FIRST_X_AXIS]);
  636.     scale[SECOND_Y_AXIS] = (ytop - ybot)/(max_array[SECOND_Y_AXIS] - min_array[SECOND_Y_AXIS]);
  637.     scale[SECOND_X_AXIS] = (xright - xleft)/(max_array[SECOND_X_AXIS] - min_array[SECOND_X_AXIS]);
  638.  
  639.    /*{{{  calculate the window in the grid for the key*/
  640.    if (lkey==1 || (lkey==-1 && key_vpos != TUNDER)) {
  641.        /* calculate space for keys to prevent grid overwrite the keys */
  642.        /* do it even if there is no grid, as do_plot will use these to position key */
  643.        key_w = key_col_wth*key_cols;
  644.        key_h = (ktitl_lines) * t->v_char + key_rows * key_entry_height;
  645.        if (lkey == -1) {
  646.            if ( key_vpos == TTOP ) {
  647.                key_yt = (int) ytop - (t->v_tic);
  648.                key_yb = key_yt - key_h;
  649.            } else {
  650.                key_yb = ybot + (t->v_tic);
  651.                key_yt = key_yb + key_h;
  652.            }
  653.            if ( key_hpos == TLEFT ) {
  654.                key_xl = xleft + (t->h_char); /* for Left just */
  655.                key_xr = key_xl + key_w;
  656.            } else if (key_hpos == TRIGHT) {
  657.                key_xr = xright - (t->h_char);  /* for Right just */
  658.                key_xl = key_xr - key_w;
  659.            } else /* TOUT */ {
  660.                /* do this here for do_plot() */
  661.                /* align right first since rmargin may be manual */
  662.                key_xr = (xsize+xoffset) * (t->xmax) - (t->h_char);
  663.                key_xl = key_xr - key_w;
  664.            }
  665.        } else {
  666.            unsigned int x,y;
  667.            map_position(&key_user_pos, &x, &y, "key");
  668.            key_xl = x - key_size_left;
  669.            key_xr = key_xl + key_w;
  670.            key_yt = y + (ktitl_lines ? t->v_char : key_entry_height)/2;
  671.            key_yb = key_yt - key_h;
  672.        }    
  673.    }
  674.    /*}}}*/
  675.  
  676. }
  677. /*}}}*/
  678.  
  679. /*{{{  dbl_raise()*/
  680. static double dbl_raise(x,y)
  681. double x;
  682. int y;
  683. {
  684. register int i=abs(y);
  685. double val=1.0;
  686.  
  687.     while (--i >= 0)
  688.         val *= x;
  689.  
  690.     if (y < 0 ) return (1.0/val);
  691.     return(val);
  692. }
  693. /*}}}*/
  694.  
  695. /*{{{  timetic_fmt()*/
  696. void
  697. timetic_format(axis,amin,amax)
  698. int axis;
  699. double amin, amax;
  700. {
  701.     double time_tic_just();
  702.     struct tm tmin, tmax;
  703.  
  704.     *ticfmt[axis]=0;  /* make sure we strcat to empty string */
  705.  
  706.     ggmtime(&tmin,(double)time_tic_just(timelevel[axis],amin));
  707.     ggmtime(&tmax,(double)time_tic_just(timelevel[axis],amax));
  708.     /*
  709.     if ( tmax.tm_year == tmin.tm_year && 
  710.         tmax.tm_mon == tmin.tm_mon && 
  711.         tmax.tm_mday == tmin.tm_mday ) {
  712.     */
  713.     if ( tmax.tm_year == tmin.tm_year && 
  714.         tmax.tm_yday == tmin.tm_yday ) {
  715.         /* same day, skip date */
  716.         if ( tmax.tm_hour != tmin.tm_hour ) {
  717.             strcpy(ticfmt[axis],"%H");
  718.         }
  719.         if ( timelevel[axis] < 3 ) {
  720.             if (strlen(ticfmt[axis]))
  721.                 strcat(ticfmt[axis],":");
  722.             strcat(ticfmt[axis],"%M");
  723.         }
  724.         if ( timelevel[axis] < 2 ) {
  725.             strcat(ticfmt[axis],":%S");
  726.         }
  727.     } else {
  728.         if ( tmax.tm_year != tmin.tm_year ) {
  729.             /* different years, include year in ticlabel */
  730.             /* check convention, day/month or month/day */
  731.             if (strchr(timefmt,'m') < strchr(timefmt,'d')) {
  732.                 strcpy(ticfmt[axis],"%m/%d/%");
  733.             } else {
  734.                 strcpy(ticfmt[axis],"%d/%m/%");
  735.             }
  736.             if ( ((int)(tmax.tm_year/100)) != ((int)(tmin.tm_year/100)) ) {
  737.                 strcat(ticfmt[axis],"Y");
  738.             } else {
  739.                 strcat(ticfmt[axis],"y");
  740.             }
  741.                          
  742.         } else {
  743.             if (strchr(timefmt,'m') < strchr(timefmt,'d')) {
  744.                 strcpy(ticfmt[axis],"%m/%d");
  745.             } else {
  746.                 strcpy(ticfmt[axis],"%d/%m");
  747.             }
  748.         }
  749.         if ( timelevel[axis] < 4 ) {
  750.             strcat(ticfmt[axis],"\n%H:%M");
  751.         }
  752.     }
  753. }
  754. /*}}}*/
  755.  
  756. /*{{{  set_tic()*/
  757. static double
  758. set_tic(l10,guide)
  759. double l10;
  760. int guide;
  761. {
  762.     double xnorm, tics, posns;
  763.  
  764.     int fl=floor(l10);
  765.     xnorm = pow(10.0,l10-fl); /* approx number of decades */
  766.  
  767.     posns = guide / xnorm;  /* approx number of tic posns per decade */
  768.     
  769.     if (posns > 10)
  770.         tics = 0.2;  /* eg 0,0.2,0.4,... */
  771.     else if (posns > 4)
  772.         tics = 0.5; /* 0,0.5,1, */
  773.     else if (posns > 1)
  774.         tics = 1; /* 0,1,2,.... */
  775.     else if (posns > 0.5)
  776.         tics = 2; /* 0, 2, 4, 6 */
  777.     else
  778.         /* getting desperate... the ceil is to make sure we
  779.          * go over rather than under - eg plot [-10:10] x*x
  780.          * gives a range of about 99.999 - tics=xnorm gives
  781.          * tics at 0, 99.99 and 109.98  - BAD !
  782.          * This way, inaccuracy the other way will round
  783.          * up (eg 0->100.0001 => tics at 0 and 101
  784.          * I think latter is better than former
  785.          */
  786.         tics = ceil(xnorm);
  787.  
  788.     return (tics * dbl_raise(10.0,fl));
  789. }
  790. /*}}}*/
  791.  
  792. /*{{{  make_tics()*/
  793. static double make_tics(axis, guide)
  794. int axis,guide;
  795. {
  796.     register double xr,tic,l10;
  797.  
  798.     xr = fabs(min_array[axis]-max_array[axis]);
  799.  
  800.     l10 = log10(xr);
  801.     tic = set_tic(l10, guide);
  802.     if (log_array[axis] && tic<1.0)
  803.         tic=1.0;
  804.     if (datatype[axis] == TIME) {
  805.         struct tm ftm, etm;
  806.         /* this is not fun */
  807.         ggmtime(&ftm,(double)min_array[axis]);
  808.         ggmtime(&etm,(double)max_array[axis]);
  809.     
  810.         timelevel[axis] = 0; /* seconds */
  811.         if ( tic > 20 ) {
  812.             /* turn tic into units of minutes */
  813.             tic = set_tic(log10(xr/60.0),guide)*60;
  814.             timelevel[axis] = 1; /* minutes */
  815.         }
  816.         if ( tic > 20*60 ) {
  817.             /* turn tic into units of hours */
  818.             tic = set_tic(log10(xr/3600.0),guide)*3600;
  819.             timelevel[axis] = 2; /* hours */
  820.         }
  821.         if ( tic > 2*3600 ) {
  822.             /* need some tickling */
  823.             tic = set_tic(log10(xr/(3*3600.0)),guide)*3*3600;
  824.         }
  825.         if ( tic > 6*3600 ) {
  826.             /* turn tic into units of days */
  827.             tic = set_tic(log10(xr/DAY_SEC),guide)*DAY_SEC;
  828.             timelevel[axis] = 3; /* days */
  829.         }
  830.         if ( tic > 3*DAY_SEC ) {
  831.             /* turn tic into units of weeks */
  832.             tic = set_tic(log10(xr/WEEK_SEC),guide)*WEEK_SEC;
  833.             if ( tic < WEEK_SEC ) { /* force */
  834.                 tic = WEEK_SEC;
  835.             }
  836.             timelevel[axis] = 4; /* weeks */
  837.         }
  838.         if ( tic > 3*WEEK_SEC ) {
  839.             /* turn tic into units of month */
  840.             tic = set_tic(log10(xr/MON_SEC),guide)*MON_SEC;
  841.             if ( tic < MON_SEC ) { /* force */
  842.                 tic = MON_SEC;
  843.             }
  844.             timelevel[axis] = 5; /* month */
  845.         }
  846.         if ( tic > 2*MON_SEC ) {
  847.             /* turn tic into units of month */
  848.             tic = set_tic(log10(xr/(3*MON_SEC)),guide)*3*MON_SEC;
  849.         }
  850.         if ( tic > 6*MON_SEC ) {
  851.             /* turn tic into units of years */
  852.             tic = set_tic(log10(xr/YEAR_SEC),guide)*YEAR_SEC;
  853.             if ( tic < (YEAR_SEC/2) ) {
  854.                 tic = YEAR_SEC / 2;
  855.             }
  856.             timelevel[axis] = 6; /* year */
  857.         }
  858.     }
  859.     return(tic);
  860. }
  861. /*}}}*/
  862.  
  863.  
  864. void do_plot(plots, pcount)
  865. struct curve_points *plots;
  866. int pcount;            /* count of plots in linked list */
  867. {
  868.  
  869. /* BODGES BEFORE I FIX IT UP */
  870. #define ytic ticstep[y_axis]
  871. #define xtic ticstep[x_axis]
  872.  
  873. register struct termentry *t = term;
  874. register int curve;
  875. int axis_zero[AXIS_ARRAY_SIZE]; /* axes in terminal coords for FIRST_X_AXIS, etc */
  876. register struct curve_points *this_plot = NULL;
  877. register int xl=0, yl=0; /* avoid gcc -Wall warning */
  878. register int key_count=0;
  879.             /* only a Pyramid would have this many registers! */
  880. struct text_label *this_label;
  881. struct arrow_def *this_arrow;
  882. TBOOLEAN scaling;
  883. char ss[MAX_LINE_LEN+1], *s, *e;
  884.  
  885.     /* so that macros for x_min etc pick up correct values
  886.      * until this is done properly
  887.      */
  888.      
  889.     x_axis=FIRST_X_AXIS; y_axis=FIRST_Y_AXIS;
  890.  
  891. /*    Apply the desired viewport offsets. */
  892.      if (y_min < y_max) {
  893.         y_min -= boff;
  894.         y_max += toff;
  895.     } else {
  896.         y_max -= boff;
  897.         y_min += toff;
  898.     }
  899.      if (x_min < x_max) {
  900.         x_min -= loff;
  901.         x_max += roff;
  902.     } else {
  903.         x_max -= loff;
  904.         x_min += roff;
  905.     }
  906.  
  907.  
  908. /*    This used be x_max == x_min, but that caused an infinite loop once. */
  909.     if (fabs(x_max - x_min) < zero)
  910.         int_error("x_min should not equal x_max!",NO_CARET);
  911.     if (fabs(y_max - y_min) < zero)
  912.         int_error("y_min should not equal y_max!",NO_CARET);
  913.  
  914.         
  915. /* INITIALIZE TERMINAL */
  916.     if (!term_init) {
  917.         (*t->init)();
  918.         term_init = TRUE;
  919.     }
  920.  
  921.      /* compute boundary for plot (xleft, xright, ytop, ybot)
  922.       * also calculates tics, since xtics depend on xleft
  923.       * but xleft depends on ytics. Boundary calculations
  924.       * depend on term->v_char etc, so terminal must be
  925.       * initialised.
  926.      */
  927.  
  928.     scaling = (*t->scale)(xsize, ysize);
  929.  
  930.     boundary(scaling,plots,pcount);
  931.  
  932.     screen_ok = FALSE;
  933.  
  934.     if (!term_graphics) {
  935.         (*t->graphics)();
  936.         term_graphics = TRUE;
  937.     } else if (multiplot && term_suspended) {
  938.         /* second plot of a multiplot, after interactive prompt */
  939.         if (t->resume)
  940.             (*t->resume)();
  941.         term_suspended = FALSE;
  942.     }
  943.         
  944. /* DRAW TICS AND GRID */
  945.     (*t->linetype)(-2); /* border linetype */
  946.     largest_polar_circle=0;
  947.  
  948.     /* select first mapping */
  949.     x_axis=FIRST_X_AXIS; y_axis=FIRST_Y_AXIS;
  950.  
  951.     /* label first y axis tics */
  952.     if (ytics) {
  953.         int axis=map_x(ZERO);
  954.         /* set the globals ytick2d_callback() needs */
  955.         tic_just=RIGHT;
  956.         if (ytics&TICS_MIRROR)
  957.             tic_mirror=xright;
  958.         else
  959.             tic_mirror= -1; /* no thank you */
  960.  
  961.         if ( (ytics&TICS_ON_AXIS) && !log_array[FIRST_X_AXIS] && inrange(axis, xleft, xright)) {
  962.             tic_start = axis;
  963.             tic_direction=-1;
  964.             /* put text at boundary if axis is close to boundary */
  965.             tic_text = ( ( (tic_start-xleft) > (3*t->h_char) ) ? tic_start : xleft) - t->h_char;
  966.         } else {
  967.             tic_start=xleft;
  968.             tic_direction=tic_in ? 1 : -1;
  969.             tic_text=xleft-t->h_char;
  970.             if (!tic_in) tic_text -= ticscale*(t->h_tic);
  971.         }
  972.         /* go for it */
  973.         gen_tics(FIRST_Y_AXIS, &yticdef,
  974.            grid&(GRID_Y|GRID_MY),
  975.            mytics, mytfreq, ytick2d_callback);
  976.  
  977.     }
  978.  
  979.     /* label first x axis tics */
  980.     if (xtics) {
  981.         int axis=map_y(ZERO);
  982.         /* set the globals xtick2d_callback() needs */
  983.         tic_just=JUST_TOP;
  984.  
  985.         if (xtics&TICS_MIRROR)
  986.             tic_mirror=ytop;
  987.         else
  988.             tic_mirror= -1; /* no thank you */
  989.         if ( (xtics&TICS_ON_AXIS) && !log_array[FIRST_Y_AXIS] && inrange(axis, ybot, ytop)) {
  990.             tic_start=axis;
  991.             tic_direction=-1;
  992.             /* put text at boundary if axis is close to boundary */
  993.             if (tic_start - ybot > 2*t->v_char)
  994.                 tic_text = tic_start - ticscale*t->v_tic - t->v_char;
  995.             else
  996.                 tic_text =  ybot - t->v_char;
  997.         } else {
  998.             tic_start=ybot;
  999.             tic_direction=tic_in ? 1 : -1;
  1000.             tic_text=ybot-t->v_char;
  1001.             if (!tic_in)
  1002.                 tic_text -= ticscale* t->v_tic;
  1003.         }
  1004.         /* go for it */
  1005.         gen_tics(FIRST_X_AXIS, &xticdef,
  1006.            grid&(GRID_X|GRID_MX),
  1007.            mxtics, mxtfreq, xtick2d_callback);
  1008.     }
  1009.  
  1010.     /* select second mapping */
  1011.     x_axis=SECOND_X_AXIS; y_axis=SECOND_Y_AXIS;
  1012.  
  1013.     /* label second y axis tics */
  1014.     if (y2tics) {
  1015.         /* set the globalss ytick2d_callback() needs */
  1016.         int axis=map_x(ZERO);
  1017.         tic_just=LEFT;
  1018.         if (y2tics&TICS_MIRROR)
  1019.             tic_mirror=xleft;
  1020.         else
  1021.             tic_mirror= -1; /* no thank you */
  1022.         if ((y2tics&TICS_ON_AXIS) && !log_array[FIRST_X_AXIS] && inrange(axis,xleft,xright)) {
  1023.             tic_start = axis;
  1024.             tic_direction=1;
  1025.             /* put text at boundary if axis is close to boundary */
  1026.             tic_text = ( ( (xright-tic_start) > (3*t->h_char) ) ? tic_start : xright) + t->h_char;
  1027.         } else {
  1028.             tic_start=xright;
  1029.             tic_direction=tic_in ? -1 : 1;
  1030.             tic_text=xright+t->h_char;
  1031.             if (!tic_in)
  1032.                 tic_text += ticscale*(t->h_tic);
  1033.         }
  1034.         /* go for it */
  1035.         gen_tics(SECOND_Y_AXIS, &y2ticdef,
  1036.           grid&(GRID_Y2|GRID_MY2),
  1037.           my2tics, my2tfreq, ytick2d_callback);
  1038.     }
  1039.  
  1040.     /* label second x axis tics */
  1041.     if (x2tics) {
  1042.         int axis=map_y(ZERO);
  1043.         /* set the globals xtick2d_callback() needs */
  1044.         tic_just=JUST_BOT;
  1045.         if (x2tics&TICS_MIRROR)
  1046.             tic_mirror=ybot;
  1047.         else
  1048.             tic_mirror= -1; /* no thank you */
  1049.         if ( (x2tics&TICS_ON_AXIS) && !log_array[SECOND_Y_AXIS] && inrange(axis,ybot,ytop)) {
  1050.             tic_start=axis;
  1051.             tic_direction=1;
  1052.             /* put text at boundary if axis is close to boundary */
  1053.             tic_text = ( ( (ytop - tic_start ) > (2*t->v_char) ) ? tic_start : ytop) + t->v_char;
  1054.         } else {
  1055.             tic_start=ytop;
  1056.             tic_direction=tic_in ? -1 : 1;
  1057.             tic_text=ytop+t->v_char + x2ticdelta;
  1058.         }
  1059.         /* go for it */
  1060.         gen_tics(SECOND_X_AXIS, &x2ticdef,
  1061.           grid&(GRID_X2|GRID_MX2),
  1062.           mx2tics, mx2tfreq, xtick2d_callback);
  1063.     }
  1064.  
  1065.     /* select first mapping */
  1066.     x_axis=FIRST_X_AXIS; y_axis=FIRST_Y_AXIS;
  1067.  
  1068. /* RADIAL LINES FOR POLAR GRID */
  1069.  
  1070.     if (polar_grid_angle) {
  1071.         double theta=0;
  1072.         int ox=map_x(0);
  1073.         int oy=map_y(0);
  1074.         (*t->linetype)(grid_linetype);
  1075.         for (theta=0; theta<6.29; theta += polar_grid_angle)
  1076.             draw_clip_line(ox,oy,
  1077.               map_x(largest_polar_circle*cos(theta)),
  1078.               map_y(largest_polar_circle*sin(theta)));
  1079.     }
  1080.  
  1081.  
  1082. /* DRAW AXES */
  1083.  
  1084.    /* after grid so that axes linetypes are on top */
  1085.  
  1086.     x_axis=FIRST_X_AXIS; y_axis=FIRST_Y_AXIS; /* chose scaling */
  1087.     axis_zero[FIRST_X_AXIS] = map_y(0.0);
  1088.     axis_zero[FIRST_Y_AXIS] = map_x(0.0); 
  1089.  
  1090.     if (axis_zero[FIRST_X_AXIS] < ybot || is_log_y)
  1091.         axis_zero[FIRST_X_AXIS] = ybot;                /* save for impulse plotting */
  1092.     else if (axis_zero[FIRST_X_AXIS] >= ytop)
  1093.         axis_zero[FIRST_X_AXIS] = ytop ;
  1094.     else if (xzeroaxis > -3) {
  1095.         (*t->linetype)(xzeroaxis);
  1096.         (*t->move)(xleft,axis_zero[FIRST_X_AXIS]);
  1097.         (*t->vector)(xright,axis_zero[FIRST_X_AXIS]);
  1098.     }
  1099.  
  1100.     if ((yzeroaxis>-3) && !is_log_x && axis_zero[FIRST_Y_AXIS] >= xleft && axis_zero[FIRST_Y_AXIS] < xright ) {
  1101.         (*t->linetype)(yzeroaxis);
  1102.         (*t->move)(axis_zero[FIRST_Y_AXIS],ybot);
  1103.         (*t->vector)(axis_zero[FIRST_Y_AXIS],ytop);
  1104.     }
  1105.  
  1106.     x_axis=SECOND_X_AXIS; y_axis=SECOND_Y_AXIS; /* chose scaling */
  1107.     axis_zero[SECOND_X_AXIS] = map_y(0.0);
  1108.     axis_zero[SECOND_Y_AXIS] = map_x(0.0); 
  1109.  
  1110.     if (axis_zero[SECOND_X_AXIS] < ybot || is_log_y2)
  1111.         axis_zero[SECOND_X_AXIS] = ybot;                /* save for impulse plotting */
  1112.     else if (axis_zero[SECOND_X_AXIS] >= ytop)
  1113.         axis_zero[SECOND_X_AXIS] = ytop ;
  1114.     else if (x2zeroaxis>-3) {
  1115.         (*t->linetype)(x2zeroaxis);
  1116.         (*t->move)(xleft,axis_zero[SECOND_X_AXIS]);
  1117.         (*t->vector)(xright,axis_zero[SECOND_X_AXIS]);
  1118.     }
  1119.  
  1120.     if ((y2zeroaxis>-3) && !is_log_x2 && axis_zero[SECOND_Y_AXIS] >= xleft && axis_zero[SECOND_Y_AXIS] < xright ) {
  1121.         (*t->linetype)(y2zeroaxis);
  1122.         (*t->move)(axis_zero[SECOND_Y_AXIS],ybot);
  1123.         (*t->vector)(axis_zero[SECOND_Y_AXIS],ytop);
  1124.     }
  1125.  
  1126.  
  1127. /* DRAW PLOT BORDER */
  1128.     (*t->linetype)(-2); /* border linetype */
  1129.     if (draw_border) {
  1130.         (*t->move)(xleft,ybot);
  1131.         if(border_south){
  1132.             (*t->vector)(xright,ybot);
  1133.         } else {
  1134.             (*t->move)(xright,ybot);
  1135.         }
  1136.         if(border_east){
  1137.             (*t->vector)(xright,ytop);
  1138.         } else {
  1139.             (*t->move)(xright,ytop);
  1140.         }
  1141.         if(border_north){ 
  1142.             (*t->vector)(xleft,ytop);
  1143.         } else {
  1144.             (*t->move)(xleft,ytop);
  1145.         }
  1146.         if(border_west){ 
  1147.             (*t->vector)(xleft,ybot);
  1148.         } else {
  1149.             (*t->move)(xleft,ybot);
  1150.         }
  1151.     }
  1152.  
  1153. /* YLABEL */
  1154.     if (*ylabel.text) {
  1155.     strcpy(ss, ylabel.text);
  1156.     if ((*t->text_angle)(1)) {
  1157.         unsigned int x=(t->v_char)+xoffset*(t->xmax)+(ylabel.xoffset)*(t->v_char);
  1158.         unsigned int y=(ytop+ybot)/2 + ylabel.yoffset*(t->h_char);
  1159.         write_multiline(x,y,ss,CENTRE, JUST_TOP, 1, ylabel.font);
  1160.         (*t->text_angle)(0);
  1161.     } else {
  1162.         /* really bottom just, but we know number of lines */
  1163.         unsigned int x=(1+ylabel.xoffset)*(t->h_char) + xoffset*(t->xmax);
  1164.         unsigned int y=ytop+(ylablin+ylabel.yoffset)*(t->v_char);
  1165.         write_multiline(x,y,ss,LEFT,JUST_TOP, 0, ylabel.font);
  1166.     }
  1167.     }
  1168.  
  1169. /* Y2LABEL */
  1170.     if (*y2label.text) {
  1171.     strcpy(ss, y2label.text);
  1172.     if ((*t->text_angle)(1)) {
  1173.         /* we worked out posn in boundary() */
  1174.         unsigned int x=y2label_x+(y2label.xoffset+1)*(t->v_char);
  1175.         unsigned int y=(ytop+ybot)/2 + y2label.yoffset*(t->h_char);
  1176.         write_multiline(x,y,ss,CENTRE, JUST_TOP, 1, y2label.font);
  1177.         (*t->text_angle)(0);
  1178.     } else {
  1179.         /* really bottom just, but we know number of lines */
  1180.         unsigned int x= y2label_x + (y2label.xoffset)*(t->h_char);
  1181.         unsigned int y=ytop+(y2lablin+y2label.yoffset)*(t->v_char);
  1182.         write_multiline(x,y,ss,RIGHT,JUST_TOP, 0, y2label.font);
  1183.     }
  1184.     }
  1185.  
  1186. /* XLABEL */
  1187.     if (*xlabel.text) {
  1188.     unsigned int x=(xright+xleft)/2 + xlabel.xoffset*(t->h_char);
  1189.     unsigned int y=ybot - (t->v_char) + xlabel.yoffset*(t->v_char);
  1190.     if (xtics&TICS_ON_BORDER) {
  1191.         y -= xticlin*(t->v_char);
  1192.         if (!tic_in)
  1193.             y -= ticscale*(t->h_tic);
  1194.     }
  1195.     strcpy(ss, xlabel.text);
  1196.     write_multiline(x,y,ss,CENTRE,JUST_TOP, 0, xlabel.font);
  1197.     }
  1198.     
  1199. /* PLACE TITLE */
  1200.     if (*title.text) {
  1201.         unsigned int x = (xleft+xright)/2 + title.xoffset * t->h_char;
  1202.         unsigned int y = ytop + (t->v_char)*top_margin + x2ticdelta + title.yoffset * t->v_char;
  1203.         strcpy(ss, title.text);
  1204.         write_multiline(x,y,ss,CENTRE, JUST_TOP, 0, title.font);
  1205.     }
  1206.  
  1207. /* X2LABEL */
  1208.     if (*x2label.text) {
  1209.         unsigned int x=(xright+xleft)/2 + x2label.xoffset*(t->h_char);
  1210.         unsigned int y=  ytop + (t->v_char)*top_margin + x2ticdelta;
  1211.         if (*title.text)
  1212.             y -= (t->v_char)*(titlelin+0.5);
  1213.         strcpy(ss, x2label.text);
  1214.         write_multiline(x,y,ss,CENTRE,JUST_TOP, 0, x2label.font);
  1215.     }
  1216.         
  1217.  
  1218. /* PLACE TIMEDATE */
  1219.     if (*timelabel.text) {
  1220.         char str[MAX_LINE_LEN+1];
  1221.         time_t now;
  1222.         unsigned int x = (timelabel.xoffset+1)*t->h_char;
  1223.         unsigned int y = yoffset * t->ymax + (timelabel.yoffset+1)*t->v_char;
  1224.  
  1225.         time(&now);
  1226.         strftime(str, MAX_LINE_LEN, timelabel.text, localtime(&now));
  1227.  
  1228.         if ((*t->text_angle)(1)) {
  1229.             write_multiline(x,y,str,LEFT, JUST_TOP, 1, timelabel.font);
  1230.             (*t->text_angle)(0);
  1231.         } else {
  1232.             write_multiline(x,y,str,LEFT, JUST_BOT, 0, timelabel.font);
  1233.           }
  1234.       }
  1235.  
  1236. /* PLACE LABELS */
  1237.     for (this_label = first_label; this_label!=NULL;
  1238.             this_label=this_label->next ) {
  1239.         unsigned int x,y;
  1240.         map_position(&this_label->place, &x, &y, "label");
  1241.         strcpy(ss, this_label->text);
  1242.         write_multiline(x, y, ss, this_label->pos, JUST_TOP, 0, this_label->font);
  1243.       }
  1244.  
  1245. /* PLACE ARROWS */
  1246.     for (this_arrow = first_arrow; this_arrow!=NULL;
  1247.         this_arrow = this_arrow->next ) {
  1248.             unsigned int sx,sy,ex,ey;
  1249.             map_position(&this_arrow->start, &sx, &sy, "arrow");
  1250.             map_position(&this_arrow->end, &ex, &ey, "arrow");
  1251.  
  1252.             (*t->linetype)(this_arrow->line-1);    /* arrow line type */
  1253.            (*t->arrow)(sx, sy, ex, ey, this_arrow->head);
  1254.     }
  1255.  
  1256. /* WORK OUT KEY SETTINGS AND DO KEY TITLE / BOX */
  1257.  
  1258.  
  1259.     if (lkey) { /* may have been cancelled if something went wrong */
  1260.         /* just use key_xl etc worked out in boundary() */
  1261.         xl = key_xl + key_size_left;
  1262.         yl = key_yt;
  1263.  
  1264. #if 0
  1265.     }
  1266.  
  1267.     if (lkey != 0 && strlen(key_title)) {
  1268.         sprintf(ss,"%s\n",key_title);
  1269.         s = ss;
  1270.         yl -= t->v_char/2;
  1271.         while( (e=(char *)strchr(s,'\n')) != NULL ) {
  1272.             *e = '\0';
  1273.             if ( key_just == JLEFT) {
  1274.                 (*t->justify_text)(LEFT);
  1275.                 (*t->put_text)(xl+key_text_left,yl,s);
  1276.             } else {
  1277.                 if ((*t->justify_text)(RIGHT)) {
  1278.                     (*t->put_text)(xl+key_text_right,yl,s);
  1279.                 } else {
  1280.                     int x=xl+key_text_right-(t->h_char)*strlen(s);
  1281.                     if (key_hpos == TOUT || inrange(x, xleft, xright))
  1282.                         (*t->put_text)(x, yl,s);
  1283.                 }
  1284.             }
  1285.             s = ++e;
  1286.             yl -= t->v_char;
  1287.         }
  1288.         yl += t->v_char/2;
  1289.     }
  1290.     yl_ref = yl -= key_entry_height/2;  /* centralise the keys */
  1291.     key_count = 0;
  1292.  
  1293.     if (lkey && key_box>-3) {
  1294.         (*t->linetype)(key_box);
  1295.         (*t->move)(key_xl,key_yb);
  1296.         (*t->vector)(key_xl,key_yt);
  1297.         (*t->vector)(key_xr,key_yt);
  1298.         (*t->vector)(key_xr,key_yb);
  1299.         (*t->vector)(key_xl,key_yb);
  1300.                 /* draw a horizontal line between key title and first entry */   /* JFi */
  1301.         (*t->move)(key_xl,key_yt - (ktitl_lines) * t->v_char);   /* JFi */
  1302.                 (*t->vector)(key_xr,key_yt - (ktitl_lines) * t->v_char); /* JFi */
  1303.     }
  1304. #else
  1305.          if (*key_title) {
  1306.              sprintf(ss,"%s\n",key_title);
  1307.              s = ss;
  1308.              yl -= t->v_char/2;
  1309.              while( (e=(char *)strchr(s,'\n')) != NULL ) {
  1310.                  *e = '\0';
  1311.                  if ( key_just == JLEFT) {
  1312.                      (*t->justify_text)(LEFT);
  1313.                      (*t->put_text)(xl+key_text_left,yl,s);
  1314.                   } else {
  1315.                      if ((*t->justify_text)(RIGHT)) {
  1316.                          (*t->put_text)(xl+key_text_right,yl,s);
  1317.                      } else {
  1318.                          int x=xl+key_text_right-(t->h_char)*strlen(s);
  1319.                          if (key_hpos == TOUT || inrange(x, xleft, xright))
  1320.                              (*t->put_text)(x, yl,s);
  1321.                      }
  1322.                  }
  1323.                  s = ++e;
  1324.                  yl -= t->v_char;
  1325.              }
  1326.              yl += t->v_char/2;
  1327.          }
  1328.          yl_ref = yl -= key_entry_height/2;  /* centralise the keys */
  1329.          key_count = 0;
  1330.      
  1331.          if (key_box>-3) {
  1332.              (*t->linetype)(key_box);
  1333.              (*t->move)(key_xl,key_yb);
  1334.              (*t->vector)(key_xl,key_yt);
  1335.              (*t->vector)(key_xr,key_yt);
  1336.              (*t->vector)(key_xr,key_yb);
  1337.              (*t->vector)(key_xl,key_yb);
  1338.                     /* draw a horizontal line between key title
  1339.                        and first entry                          */             /* JFi */
  1340.                 (*t->move)  (key_xl,key_yt - (ktitl_lines) * t->v_char); /* JFi */
  1341.                       (*t->vector)(key_xr,key_yt - (ktitl_lines) * t->v_char); /* JFi */
  1342.          }
  1343.      } /* lkey */
  1344. #endif
  1345.  
  1346. /* DRAW CURVES */
  1347.  
  1348.     this_plot = plots;
  1349.      for (curve = 0; curve < pcount; this_plot = this_plot->next_cp, curve++) {
  1350.           int localkey = lkey; /* a local copy */
  1351.  
  1352.         /* set scaling for this plot's axes */
  1353.         x_axis=this_plot->x_axis;
  1354.         y_axis=this_plot->y_axis;
  1355.  
  1356.         (*t->linetype)(this_plot->line_type);
  1357.  
  1358.         if (this_plot->title && !*this_plot->title) {
  1359.             localkey = 0;
  1360.         } else {
  1361.             (*t->linetype)(this_plot->line_type);
  1362.             if (localkey != 0 && this_plot->title) {
  1363.   
  1364.                 key_count++;
  1365.                 if ( key_just == JLEFT) {
  1366.                     (*t->justify_text)(LEFT);
  1367.                     (*t->put_text)(xl+key_text_left,
  1368.                              yl,this_plot->title);
  1369.                 } else {
  1370.                     if ((*t->justify_text)(RIGHT)) {
  1371.                         (*t->put_text)(xl+key_text_right,
  1372.                             yl,this_plot->title);
  1373.                     } else {
  1374.                         int x=xl+key_text_right-(t->h_char)*strlen(this_plot->title);
  1375.                         if (key_hpos == TOUT ||
  1376.                              i_inrange(x, xleft, xright))
  1377.                          (*t->put_text)(x, yl,this_plot->title);
  1378.                     }
  1379.                 }
  1380.             }
  1381.  
  1382.         }
  1383.         switch(this_plot->plot_style) {
  1384.             case IMPULSES: {
  1385.                if (localkey != 0 && this_plot->title) {
  1386.                   (*t->move)(xl+key_sample_left,yl);
  1387.                   (*t->vector)(xl+key_sample_right,yl);
  1388.                }
  1389.                plot_impulses(this_plot, axis_zero[y_axis], axis_zero[x_axis]);
  1390.                break;
  1391.             }
  1392.             case LINES: {
  1393.                if (localkey != 0 && this_plot->title) {
  1394.                   (*t->move)(xl+key_sample_left,yl);
  1395.                   (*t->vector)(xl+key_sample_right,yl);
  1396.                }
  1397.                plot_lines(this_plot);
  1398.                break;
  1399.             }
  1400. /* JG */        case STEPS: {
  1401.                if (localkey != 0 && this_plot->title) {
  1402.                   (*t->move)(xl+key_sample_left,yl);
  1403.                   (*t->vector)(xl+key_sample_right,yl);
  1404.                }
  1405.                plot_steps(this_plot);
  1406.                break;
  1407.             }
  1408. /* JG */        case FSTEPS: {
  1409.                if (localkey != 0 && this_plot->title) {
  1410.                   (*t->move)(xl+key_sample_left,yl);
  1411.                   (*t->vector)(xl+key_sample_right,yl);
  1412.                }
  1413.                plot_fsteps(this_plot);
  1414.                break;
  1415.             }
  1416.             case POINTSTYLE: {
  1417.                if (localkey != 0 && this_plot->title) {
  1418.                   (*t->point)(xl+key_point_offset,yl,
  1419.                             this_plot->point_type);
  1420.                }
  1421.                plot_points(this_plot);
  1422.                break;
  1423.             }
  1424.               case LINESPOINTS: {
  1425.                  /* put lines */
  1426.                if (localkey != 0 && this_plot->title) {
  1427.                   (*t->move)(xl+key_sample_left,yl);
  1428.                   (*t->vector)(xl+key_sample_right,yl);
  1429.                }
  1430.                plot_lines(this_plot);
  1431.  
  1432.                /* put points */
  1433.                if (key != 0 && this_plot->title) {
  1434.                   (*t->point)(xl+key_point_offset,yl,
  1435.                             this_plot->point_type);
  1436.                }
  1437.                plot_points(this_plot);
  1438.                break;
  1439.             }
  1440.               case DOTS: {
  1441.                if (localkey != 0 && this_plot->title) {
  1442.                     (*t->point)(xl+key_point_offset,yl, -1);
  1443.                  }
  1444.                  plot_dots(this_plot);
  1445.                break;
  1446.             }
  1447.                     case YERRORBARS: {
  1448.                            if (localkey != 0 && this_plot->title) {
  1449.                                   (*t->point)(xl+key_point_offset,yl,
  1450.                                                     this_plot->point_type);
  1451.                            }
  1452.                            plot_points(this_plot);
  1453.  
  1454.                            /* for functions, just like POINTSTYLE */
  1455.                            if (this_plot->plot_type == DATA) {
  1456.                                   if (localkey != 0 && this_plot->title) {
  1457.                                          (*t->move)(xl+key_sample_left,yl);
  1458.                                          (*t->vector)(xl+key_sample_right,yl);
  1459.                                          if(bar_size>0.0){
  1460.                                             (*t->move)(xl+key_sample_left,yl+ERRORBARTIC);
  1461.                                             (*t->vector)(xl+key_sample_left,yl-ERRORBARTIC);
  1462.                                             (*t->move)(xl+key_sample_right,yl+ERRORBARTIC);
  1463.                                             (*t->vector)(xl+key_sample_right,yl-ERRORBARTIC);
  1464.                                          }
  1465.                                   }
  1466.                                   plot_bars(this_plot);
  1467.                            }
  1468.                            break;
  1469.                     }
  1470.                     case XERRORBARS: {
  1471.                            if (localkey != 0 && this_plot->title) {
  1472.                                   (*t->point)(xl+2*(t->h_char),yl,
  1473.                                                     this_plot->point_type);
  1474.                            }
  1475.                            plot_points(this_plot);
  1476.  
  1477.                            /* for functions, just like POINTSTYLE */
  1478.                            if (this_plot->plot_type == DATA) {
  1479.                if (localkey != 0 && this_plot->title) {
  1480.                                          (*t->move)(xl+key_sample_left,yl);
  1481.                                          (*t->vector)(xl+key_sample_right,yl);
  1482.                                          if(bar_size > 0.0){
  1483.                                             (*t->move)(xl+key_sample_left,yl+ERRORBARTIC);
  1484.                                             (*t->vector)(xl+key_sample_left,yl-ERRORBARTIC);
  1485.                                             (*t->move)(xl+key_sample_right,yl+ERRORBARTIC);
  1486.                                             (*t->vector)(xl+key_sample_right,yl-ERRORBARTIC);
  1487.                                          }
  1488.                                   }
  1489.                                   plot_bars(this_plot);
  1490.                            }
  1491.                            break;
  1492.                     }
  1493.                     case XYERRORBARS: {
  1494.                            if (localkey != 0 && this_plot->title) {
  1495.                   (*t->point)(xl+2*(t->h_char),yl,
  1496.                             this_plot->point_type);
  1497.                }
  1498.                plot_points(this_plot);
  1499.  
  1500.                /* for functions, just like POINTSTYLE */
  1501.                if (this_plot->plot_type == DATA) {
  1502.                    if (localkey != 0 && this_plot->title) {
  1503.                                          (*t->move)(xl+key_sample_left,yl);
  1504.                                          (*t->vector)(xl+key_sample_right,yl);
  1505.                                          if(bar_size > 0.0){
  1506.                                             (*t->move)(xl+key_sample_left,yl+ERRORBARTIC);
  1507.                                             (*t->vector)(xl+key_sample_left,yl-ERRORBARTIC);
  1508.                                             (*t->move)(xl+key_sample_right,yl+ERRORBARTIC);
  1509.                                             (*t->vector)(xl+key_sample_right,yl-ERRORBARTIC);
  1510.                                          }
  1511.                                   }
  1512.                   plot_bars(this_plot);
  1513.                }
  1514.                break;
  1515.             }
  1516.                     case BOXXYERROR: {
  1517.                            if (localkey != 0 && this_plot->title) {
  1518.                                   (*t->point)(xl+key_point_offset,yl,
  1519.                                                     this_plot->point_type);
  1520.                            }
  1521.                            plot_boxes(this_plot,axis_zero[x_axis]);
  1522.                            break;
  1523.                     }
  1524.             case BOXERROR: {
  1525.                if (this_plot->plot_type == DATA) {
  1526.                    if (localkey != 0 && this_plot->title) {
  1527.                                          (*t->move)(xl+key_sample_left,yl);
  1528.                                          (*t->vector)(xl+key_sample_right,yl);
  1529.                                          if(bar_size > 0.0){
  1530.                                             (*t->move)(xl+key_sample_left,yl+ERRORBARTIC);
  1531.                                             (*t->vector)(xl+key_sample_left,yl-ERRORBARTIC);
  1532.                                             (*t->move)(xl+key_sample_right,yl+ERRORBARTIC);
  1533.                                             (*t->vector)(xl+key_sample_right,yl-ERRORBARTIC);
  1534.                                          }
  1535.                                   }
  1536.                   plot_bars(this_plot);
  1537.                }
  1538.             }
  1539.             /* no break */
  1540.             case BOXES: {
  1541.                if (localkey != 0 && this_plot->title) {
  1542.                   (*t->move)(xl+key_sample_left,yl);
  1543.                   (*t->vector)(xl+key_sample_right,yl);
  1544.                }
  1545.                plot_boxes(this_plot,axis_zero[x_axis]);
  1546.                break;
  1547.             }
  1548.  
  1549.             case VECTOR:
  1550.                if (localkey != 0 && this_plot->title) {
  1551.                   (*t->move)(xl+key_sample_left,yl);
  1552.                   (*t->vector)(xl+key_sample_right,yl);
  1553.                }
  1554.                plot_vectors(this_plot);
  1555.                break;
  1556.         }
  1557.         if (localkey && this_plot->title) {
  1558.             if (key_count >= key_rows ) {
  1559.             yl = yl_ref;
  1560.             xl += key_col_wth;
  1561.             key_count = 0;
  1562.             } else 
  1563.                 yl = yl - key_entry_height;
  1564.         }
  1565.     }
  1566.     if (!multiplot){
  1567.         (*t->text)();
  1568.         term_graphics=FALSE;
  1569.     }
  1570.     (void) fflush(outfile);
  1571. }
  1572.  
  1573.  
  1574. /* BODGES */
  1575. #undef ytic
  1576. #undef xtic
  1577.  
  1578. /* plot_impulses:
  1579.  * Plot the curves in IMPULSES style
  1580.  */
  1581.  
  1582. static void
  1583. plot_impulses(plot, yaxis_x, xaxis_y)
  1584.     struct curve_points *plot;
  1585.     int yaxis_x, xaxis_y;
  1586. {
  1587.     int i;
  1588.     int x,y;
  1589.     struct termentry *t = term;
  1590.  
  1591.     for (i = 0; i < plot->p_count; i++) {
  1592.        switch (plot->points[i].type) {
  1593.           case INRANGE: {
  1594.              x = map_x(plot->points[i].x);
  1595.              y = map_y(plot->points[i].y);
  1596.              break;
  1597.           }
  1598.           case OUTRANGE: {
  1599.              if (!inrange(plot->points[i].x, x_min,x_max))
  1600.                continue;
  1601.              x = map_x(plot->points[i].x);
  1602.              if ((y_min < y_max 
  1603.                  && plot->points[i].y < y_min)
  1604.                 || (y_max < y_min 
  1605.                     && plot->points[i].y > y_min))
  1606.                y = map_y(y_min);
  1607.              else
  1608.                y = map_y(y_max);
  1609.             
  1610.              break;
  1611.           }
  1612.           default:        /* just a safety */
  1613.           case UNDEFINED: {
  1614.              continue;
  1615.           }
  1616.        }
  1617.                     
  1618.        if (polar)
  1619.           (*t->move)(yaxis_x,xaxis_y);
  1620.        else
  1621.           (*t->move)(x,xaxis_y);
  1622.        (*t->vector)(x,y);
  1623.     }
  1624.  
  1625. }
  1626.  
  1627. /* plot_lines:
  1628.  * Plot the curves in LINES style
  1629.  */
  1630. static void
  1631. plot_lines(plot)
  1632.     struct curve_points *plot;
  1633. {
  1634.     int i;                /* point index */
  1635.     int x,y;                /* point in terminal coordinates */
  1636.     struct termentry *t = term;
  1637.     enum coord_type prev = UNDEFINED; /* type of previous point */
  1638.     double ex, ey;            /* an edge point */
  1639.     double lx[2], ly[2];        /* two edge points */
  1640.  
  1641.     for (i = 0; i < plot->p_count; i++) {
  1642.        switch (plot->points[i].type) {
  1643.           case INRANGE: {
  1644.              x = map_x(plot->points[i].x);
  1645.              y = map_y(plot->points[i].y);
  1646.  
  1647.              if (prev == INRANGE) {
  1648.                 (*t->vector)(x,y);
  1649.              } else if (prev == OUTRANGE) {
  1650.                 /* from outrange to inrange */
  1651.                 if (!clip_lines1) {
  1652.                     (*t->move)(x,y);
  1653.                 } else {
  1654.                     edge_intersect(plot->points, i, &ex, &ey);
  1655.                     (*t->move)(map_x(ex), map_y(ey));
  1656.                     (*t->vector)(x,y);
  1657.                 }
  1658.              } else {        /* prev == UNDEFINED */
  1659.                 (*t->move)(x,y);
  1660.                 (*t->vector)(x,y);
  1661.              }
  1662.                     
  1663.              break;
  1664.           }
  1665.           case OUTRANGE: {
  1666.              if (prev == INRANGE) {
  1667.                 /* from inrange to outrange */
  1668.                 if (clip_lines1) {
  1669.                     edge_intersect(plot->points, i, &ex, &ey);
  1670.                     (*t->vector)(map_x(ex), map_y(ey));
  1671.                 }
  1672.              } else if (prev == OUTRANGE) {
  1673.                 /* from outrange to outrange */
  1674.                 if (clip_lines2) {
  1675.                     if (two_edge_intersect(plot->points, i, lx, ly)) {
  1676.                        (*t->move)(map_x(lx[0]), map_y(ly[0]));
  1677.                        (*t->vector)(map_x(lx[1]), map_y(ly[1]));
  1678.                     }
  1679.                 }
  1680.              }
  1681.              break;
  1682.           }
  1683.           default:        /* just a safety */
  1684.           case UNDEFINED: {
  1685.              break;
  1686.           }
  1687.        }
  1688.        prev = plot->points[i].type;
  1689.     }
  1690. }
  1691.  
  1692. /* XXX - JG  */
  1693. /* plot_steps:                
  1694.  * Plot the curves in STEPS style
  1695.  */
  1696. static void
  1697. plot_steps(plot)
  1698. struct curve_points *plot;
  1699. {
  1700.     int i;                /* point index */
  1701.     int x,y;                /* point in terminal coordinates */
  1702.     struct termentry *t = term;
  1703.     enum coord_type prev = UNDEFINED;    /* type of previous point */
  1704.     double ex, ey;            /* an edge point */
  1705.     double lx[2], ly[2];        /* two edge points */
  1706.     int yprev=0;                 /* previous point coordinates */
  1707.  
  1708.     for (i = 0; i < plot->p_count; i++) {
  1709.        switch (plot->points[i].type) {
  1710.           case INRANGE: {
  1711.              x = map_x(plot->points[i].x);
  1712.              y = map_y(plot->points[i].y);
  1713.  
  1714.              if (prev == INRANGE) {
  1715.                 (*t->vector)(x,yprev);
  1716.                 (*t->vector)(x,y);
  1717.              } else if (prev == OUTRANGE) {
  1718.                 /* from outrange to inrange */
  1719.                 if (!clip_lines1) {
  1720.                     (*t->move)(x,y);
  1721.                 } else {        /* find edge intersection */
  1722.                     edge_intersect_steps(plot->points, i, &ex, &ey);
  1723.                     (*t->move)(map_x(ex), map_y(ey));
  1724.                     (*t->vector)(x,map_y(ey));
  1725.                     (*t->vector)(x,y);
  1726.                 }
  1727.              } else {        /* prev == UNDEFINED */
  1728.                 (*t->move)(x,y);
  1729.                 (*t->vector)(x,y);
  1730.              }
  1731.             yprev = y;
  1732.              break;
  1733.           }
  1734.           case OUTRANGE: {
  1735.              if (prev == INRANGE) {
  1736.                 /* from inrange to outrange */
  1737.                 if (clip_lines1) {
  1738.                     edge_intersect_steps(plot->points, i, &ex, &ey);
  1739.                     (*t->vector)(map_x(ex), yprev);
  1740.                     (*t->vector)(map_x(ex), map_y(ey));
  1741.                 }
  1742.              } else if (prev == OUTRANGE) {
  1743.                 /* from outrange to outrange */
  1744.                 if (clip_lines2) {
  1745.                     if (two_edge_intersect_steps(plot->points, i, lx, ly)) {
  1746.                        (*t->move)(map_x(lx[0]), map_y(ly[0]));
  1747.                        (*t->vector)(map_x(lx[1]), map_y(ly[0]));
  1748.                        (*t->vector)(map_x(lx[1]), map_y(ly[1]));
  1749.                     }
  1750.                 }
  1751.              }
  1752.              break;
  1753.           }
  1754.           default:        /* just a safety */
  1755.           case UNDEFINED: {
  1756.              break;
  1757.           }
  1758.        }
  1759.        prev  = plot->points[i].type;
  1760.     }
  1761. }
  1762. /* XXX - HOE  */
  1763. /* plot_fsteps:                
  1764.  * Plot the curves in STEPS style by step on forward yvalue
  1765.  */
  1766. static void
  1767. plot_fsteps(plot)
  1768. struct curve_points *plot;
  1769. {
  1770.     int i;                /* point index */
  1771.     int x,y;                /* point in terminal coordinates */
  1772.     struct termentry *t = term;
  1773.     enum coord_type prev = UNDEFINED;    /* type of previous point */
  1774.     double ex, ey;            /* an edge point */
  1775.     double lx[2], ly[2];        /* two edge points */
  1776.     int xprev = 0;        /* previous point coordinates */
  1777.  
  1778.     for (i = 0; i < plot->p_count; i++) {
  1779.        switch (plot->points[i].type) {
  1780.           case INRANGE: {
  1781.              x = map_x(plot->points[i].x);
  1782.              y = map_y(plot->points[i].y);
  1783.  
  1784.              if (prev == INRANGE) {
  1785.                 (*t->vector)(xprev,y);
  1786.                 (*t->vector)(x,y);
  1787.              } else if (prev == OUTRANGE) {
  1788.                 /* from outrange to inrange */
  1789.                 if (!clip_lines1) {
  1790.                     (*t->move)(x,y);
  1791.                 } else {        /* find edge intersection */
  1792.                     edge_intersect_fsteps(plot->points, i, &ex, &ey);
  1793.                     (*t->move)(map_x(ex), map_y(ey));
  1794.                     (*t->vector)(map_x(ex),y);
  1795.                     (*t->vector)(x,y);
  1796.                 }
  1797.              } else {        /* prev == UNDEFINED */
  1798.                 (*t->move)(x,y);
  1799.                 (*t->vector)(x,y);
  1800.              }
  1801.              xprev = x;
  1802.              break;
  1803.           }
  1804.           case OUTRANGE: {
  1805.              if (prev == INRANGE) {
  1806.                 /* from inrange to outrange */
  1807.                 if (clip_lines1) {
  1808.                     edge_intersect_fsteps(plot->points, i, &ex, &ey);
  1809.                     (*t->vector)(xprev,map_y(ey));
  1810.                     (*t->vector)(map_x(ex), map_y(ey));
  1811.                 }
  1812.              } else if (prev == OUTRANGE) {
  1813.                 /* from outrange to outrange */
  1814.                 if (clip_lines2) {
  1815.                     if (two_edge_intersect_fsteps(plot->points, i, lx, ly)) {
  1816.                        (*t->move)(map_x(lx[0]), map_y(ly[0]));
  1817.                        (*t->vector)(map_x(lx[0]), map_y(ly[1]));
  1818.                        (*t->vector)(map_x(lx[1]), map_y(ly[1]));
  1819.                     }
  1820.                 }
  1821.              }
  1822.              break;
  1823.           }
  1824.           default:        /* just a safety */
  1825.           case UNDEFINED: {
  1826.              break;
  1827.           }
  1828.        }
  1829.        prev  = plot->points[i].type;
  1830.     }
  1831. }
  1832.  
  1833.  
  1834. /* plot_bars:
  1835.  * Plot the curves in ERRORBARS style
  1836.  *  we just plot the bars; the points are plotted in plot_points
  1837.  */
  1838. static void
  1839. plot_bars(plot)
  1840.     struct curve_points *plot;
  1841. {
  1842.     int i;                /* point index */
  1843.     struct termentry *t = term;
  1844.     double x, y;                        /* position of the bar */
  1845.     double ylow, yhigh;        /* the ends of the bars */
  1846.     double xlow, xhigh;
  1847.     unsigned int xM, ylowM, yhighM; /* the mapped version of above */
  1848.     unsigned int yM, xlowM, xhighM;
  1849.     TBOOLEAN low_inrange, high_inrange;
  1850.     int tic = ERRORBARTIC;
  1851.     
  1852. /* Limitation: no boxes with x errorbars */
  1853.  
  1854.     if ((plot->plot_style == YERRORBARS) || (plot->plot_style == XYERRORBARS) ||
  1855.          (plot->plot_style == BOXERROR)){
  1856. /* Draw the vertical part of the bar */
  1857.     for (i = 0; i < plot->p_count; i++) {
  1858.        /* undefined points don't count */
  1859.        if (plot->points[i].type == UNDEFINED)
  1860.         continue;
  1861.  
  1862.        /* check to see if in xrange */
  1863.        x = plot->points[i].x;
  1864.        if (! inrange(x, x_min, x_max))
  1865.         continue;
  1866.        xM = map_x(x);
  1867.  
  1868.        /* find low and high points of bar, and check yrange */
  1869.        yhigh = plot->points[i].yhigh;
  1870.        ylow = plot->points[i].ylow;
  1871.  
  1872.        high_inrange = inrange(yhigh, y_min,y_max);
  1873.        low_inrange = inrange(ylow, y_min,y_max);
  1874.  
  1875.        /* compute the plot position of yhigh */
  1876.        if (high_inrange)
  1877.         yhighM = map_y(yhigh);
  1878.        else if (samesign(yhigh-y_max, y_max-y_min))
  1879.         yhighM = map_y(y_max);
  1880.        else
  1881.         yhighM = map_y(y_min);
  1882.        
  1883.        /* compute the plot position of ylow */
  1884.        if (low_inrange)
  1885.         ylowM = map_y(ylow);
  1886.        else if (samesign(ylow-y_max, y_max-y_min))
  1887.         ylowM = map_y(y_max);
  1888.        else
  1889.         ylowM = map_y(y_min);
  1890.  
  1891.        if (!high_inrange && !low_inrange && ylowM == yhighM)
  1892.         /* both out of range on the same side */
  1893.           continue;
  1894.  
  1895.        /* by here everything has been mapped */
  1896.        (*t->move)(xM, ylowM);
  1897.        (*t->vector)(xM, yhighM); /* draw the main bar */
  1898.            if(bar_size > 0.0){
  1899.        (*t->move)((unsigned int)(xM-bar_size*tic), ylowM); /* draw the bottom tic */
  1900.        (*t->vector)((unsigned int)(xM+bar_size*tic), ylowM);
  1901.        (*t->move)((unsigned int)(xM-bar_size*tic), yhighM); /* draw the top tic */
  1902.        (*t->vector)((unsigned int)(xM+bar_size*tic), yhighM);
  1903.     }
  1904.     } /* for loop */
  1905.     } /* if yerrorbars OR xyerrorbars */
  1906.  
  1907.     if ((plot->plot_style == XERRORBARS) || (plot->plot_style == XYERRORBARS)){
  1908. /* Draw the horizontal part of the bar */
  1909.     for (i = 0; i < plot->p_count; i++) {
  1910.            /* undefined points don't count */
  1911.            if (plot->points[i].type == UNDEFINED)
  1912.                 continue;
  1913.  
  1914.            /* check to see if in yrange */
  1915.            y = plot->points[i].y;
  1916.            if (! inrange(y, y_min, y_max))
  1917.                 continue;
  1918.            yM = map_y(y);
  1919.  
  1920.            /* find low and high points of bar, and check xrange */
  1921.            xhigh = plot->points[i].xhigh;
  1922.            xlow = plot->points[i].xlow;
  1923.  
  1924.            high_inrange = inrange(xhigh, x_min,x_max);
  1925.            low_inrange = inrange(xlow, x_min,x_max);
  1926.  
  1927.            /* compute the plot position of xhigh */
  1928.            if (high_inrange)
  1929.                 xhighM = map_x(xhigh);
  1930.            else if (samesign(xhigh-x_max, x_max-x_min))
  1931.                 xhighM = map_x(x_max);
  1932.            else
  1933.                 xhighM = map_x(x_min);
  1934.  
  1935.            /* compute the plot position of xlow */
  1936.            if (low_inrange)
  1937.                 xlowM = map_x(xlow);
  1938.            else if (samesign(xlow-x_max, x_max-x_min))
  1939.                 xlowM = map_x(x_max);
  1940.            else
  1941.                 xlowM = map_x(x_min);
  1942.  
  1943.            if (!high_inrange && !low_inrange && xlowM == xhighM)
  1944.                 /* both out of range on the same side */
  1945.                   continue;
  1946.  
  1947.            /* by here everything has been mapped */
  1948.            (*t->move)(xlowM, yM);
  1949.            (*t->vector)(xhighM, yM); /* draw the main bar */
  1950.            if(bar_size > 0.0){
  1951.               (*t->move)(xlowM, (unsigned int)(yM-bar_size*tic)); /* draw the left tic */
  1952.               (*t->vector)(xlowM, (unsigned int)(yM+bar_size*tic));
  1953.               (*t->move)(xhighM, (unsigned int)(yM-bar_size*tic)); /* draw the right tic */
  1954.               (*t->vector)(xhighM, (unsigned int)(yM+bar_size*tic));
  1955.            }
  1956.     } /* for loop */
  1957.     } /* if xerrorbars OR xyerrorbars */
  1958. }
  1959.  
  1960. /* plot_boxes:
  1961.  * Plot the curves in BOXES style
  1962.  */
  1963. static void
  1964. plot_boxes(plot,xaxis_y)
  1965.     struct curve_points *plot;
  1966.     int xaxis_y;
  1967. {
  1968.     int i;                /* point index */
  1969.     int xl,xr,yt;            /* point in terminal coordinates */
  1970.     double dxl,dxr,dyt;
  1971.     struct termentry *t = term;
  1972.     enum coord_type prev = UNDEFINED; /* type of previous point */
  1973.     TBOOLEAN boxxy = (plot->plot_style == BOXXYERROR);
  1974.  
  1975.     for (i = 0; i < plot->p_count; i++) {
  1976.        switch (plot->points[i].type) {
  1977.           case OUTRANGE:
  1978.           case INRANGE: {
  1979.             if (plot->points[i].z<0.0) {
  1980.                if (boxwidth<0.0) {
  1981.                     /* calculate width */
  1982.                     if (prev!=UNDEFINED)
  1983.                         dxl = (plot->points[i-1].x - plot->points[i].x)/2.0;
  1984.                     else
  1985.                         dxl = 0.0;
  1986.                     if (i < plot->p_count-1) {
  1987.                         if (plot->points[i+1].type!=UNDEFINED)
  1988.                             dxr = (plot->points[i+1].x - plot->points[i].x)/2.0;
  1989.                         else
  1990.                             dxr = -dxl;
  1991.                     }
  1992.                     else {
  1993.                         dxr = -dxl;
  1994.                     }
  1995.                     if (prev==UNDEFINED)
  1996.                         dxl = -dxr;
  1997.                 }
  1998.                 else {
  1999.                     dxr = boxwidth/2.0;
  2000.                     dxl = -dxr;
  2001.                 }
  2002.             }
  2003.             else {
  2004.                 dxr = plot->points[i].z/2.0;
  2005.                 dxl = -dxr;
  2006.             }
  2007.  
  2008.                         if (boxxy) {
  2009.                                 dxr = plot->points[i].xhigh;
  2010.                                 dxl = plot->points[i].xlow;
  2011.                                 dyt = plot->points[i].yhigh;
  2012.                                 xaxis_y = map_y(plot->points[i].ylow);
  2013.                         }
  2014.                         else {
  2015.             dxl= plot->points[i].x+dxl;
  2016.             dxr= plot->points[i].x+dxr;
  2017.             dyt= plot->points[i].y;
  2018.                         }
  2019.  
  2020.             /* clip to border */
  2021.             if ((y_min < y_max  && dyt < y_min)
  2022.                 || (y_max < y_min  && dyt > y_min))
  2023.                dyt = y_min;
  2024.             if ((y_min < y_max  && dyt > y_max)
  2025.                 || (y_max<y_min  && dyt < y_max))
  2026.                dyt = y_max;
  2027.             if ((x_min < x_max  && dxr < x_min)
  2028.                 || (x_max < x_min  && dxr > x_min))
  2029.                dxr = x_min;
  2030.             if ((x_min < x_max  && dxr > x_max)
  2031.                 || (x_max<x_min  && dxr < x_max))
  2032.                dxr = x_max;
  2033.             if ((x_min < x_max  && dxl < x_min)
  2034.                 || (x_max < x_min  && dxl > x_min))
  2035.                dxl = x_min;
  2036.             if ((x_min < x_max  && dxl > x_max)
  2037.                 || (x_max<x_min  && dxl < x_max))
  2038.                dxl = x_max;
  2039.  
  2040.             xl= map_x(dxl);
  2041.             xr= map_x(dxr);
  2042.             yt = map_y(dyt);
  2043.  
  2044.             (*t->move)(xl,xaxis_y);
  2045.             (*t->vector)(xl,yt);
  2046.             (*t->vector)(xr,yt);
  2047.             (*t->vector)(xr,xaxis_y);
  2048.             (*t->vector)(xl,xaxis_y);
  2049.             break;
  2050.           }
  2051.           default:        /* just a safety */
  2052.           case UNDEFINED: {
  2053.              break;
  2054.           }
  2055.        }
  2056.        prev = plot->points[i].type;
  2057.     }
  2058. }
  2059.  
  2060. /* plot_points:
  2061.  * Plot the curves in POINTSTYLE style
  2062.  */
  2063. static void
  2064. plot_points(plot)
  2065.     struct curve_points *plot;
  2066. {
  2067.     int i;
  2068.     int x,y;
  2069.     struct termentry *t = term;
  2070.  
  2071.     for (i = 0; i < plot->p_count; i++) {
  2072.        if (plot->points[i].type == INRANGE) {
  2073.           x = map_x(plot->points[i].x);
  2074.           y = map_y(plot->points[i].y);
  2075.           /* do clipping if necessary */
  2076.           if (!clip_points ||
  2077.              (   x >= xleft + p_width  && y >= ybot + p_height 
  2078.               && x <= xright - p_width && y <= ytop - p_height))
  2079.             (*t->point)(x,y, plot->point_type);
  2080.        }
  2081.     }
  2082. }
  2083.  
  2084. /* plot_dots:
  2085.  * Plot the curves in DOTS style
  2086.  */
  2087. static void
  2088. plot_dots(plot)
  2089.     struct curve_points *plot;
  2090. {
  2091.     int i;
  2092.     int x,y;
  2093.     struct termentry *t = term;
  2094.  
  2095.     for (i = 0; i < plot->p_count; i++) {
  2096.        if (plot->points[i].type == INRANGE) {
  2097.           x = map_x(plot->points[i].x);
  2098.           y = map_y(plot->points[i].y);
  2099.           /* point type -1 is a dot */
  2100.           (*t->point)(x,y, -1);
  2101.        }
  2102.     }
  2103. }
  2104.  
  2105. /* plot_vectors:
  2106.  * Plot the curves in VECTORS style
  2107.  */
  2108. static void
  2109. plot_vectors(plot)
  2110.     struct curve_points *plot;
  2111. {
  2112.     int i;
  2113.     int x1,y1,x2,y2;
  2114.     struct termentry *t = term;
  2115.  
  2116.     for (i = 0; i < plot->p_count; i++) {
  2117.        if (plot->points[i].type == INRANGE) {
  2118.           x1 = map_x(plot->points[i].xlow);
  2119.           y1 = map_y(plot->points[i].ylow);
  2120.           x2 = map_x(plot->points[i].xhigh);
  2121.           y2 = map_y(plot->points[i].yhigh);
  2122.           (*t->arrow)(x1,y1, x2,y2, TRUE);
  2123.        }
  2124.     }
  2125. }
  2126.  
  2127. /* single edge intersection algorithm */
  2128. /* Given two points, one inside and one outside the plot, return
  2129.  * the point where an edge of the plot intersects the line segment defined 
  2130.  * by the two points.
  2131.  */
  2132. static void
  2133. edge_intersect(points, i, ex, ey)
  2134.     struct coordinate GPHUGE *points; /* the points array */
  2135.     int i;                /* line segment from point i-1 to point i */
  2136.     double *ex, *ey;        /* the point where it crosses an edge */
  2137. {
  2138.     /* global x_min, x_max, y_min, x_max */
  2139.     double ix = points[i-1].x;
  2140.     double iy = points[i-1].y;
  2141.     double ox = points[i].x;
  2142.     double oy = points[i].y;
  2143.     double x, y;            /* possible intersection point */
  2144.  
  2145.     if(points[i].type == INRANGE)
  2146.       {
  2147.     /* swap points around so that ix/ix/iz are INRANGE and ox/oy/oz are OUTRANGE */
  2148.     x = ix;ix = ox;ox = x;
  2149.     y = iy;iy = oy;oy = y;
  2150.       }
  2151.  
  2152.     /* nasty degenerate cases, effectively drawing to an infinity point (?)
  2153.        cope with them here, so don't process them as a "real" OUTRANGE point 
  2154.  
  2155.        If more than one coord is -VERYLARGE, then can't ratio the "infinities"
  2156.        so drop out by returning the INRANGE point. 
  2157.  
  2158.        Obviously, only need to test the OUTRANGE point (coordinates) */
  2159.     if(ox == -VERYLARGE || oy == -VERYLARGE)
  2160.       {
  2161.     *ex = ix;
  2162.     *ey = iy;
  2163.  
  2164.     if(ox == -VERYLARGE)
  2165.       {
  2166.         /* can't get a direction to draw line, so simply return INRANGE point */
  2167.         if(oy == -VERYLARGE)
  2168.           return;
  2169.  
  2170.         *ex = x_min;
  2171.         return;
  2172.       }
  2173.  
  2174.     /* obviously oy is -VERYLARGE and ox != -VERYLARGE */
  2175.     *ey = y_min;
  2176.     return;
  2177.       }
  2178.  
  2179.     /*
  2180.      * Can't have case (ix == ox && iy == oy) as one point
  2181.      * is INRANGE and one point is OUTRANGE.
  2182.      */    
  2183.     if (iy == oy) {
  2184.        /* horizontal line */
  2185.        /* assume inrange(iy, y_min, y_max) */
  2186.        *ey = iy;        /* == oy */
  2187.  
  2188.        if (inrange(x_max, ix, ox))
  2189.         *ex = x_max;
  2190.        else if (inrange(x_min, ix, ox))
  2191.         *ex = x_min;
  2192.        else {
  2193.         graph_error("error in edge_intersect");
  2194.        }
  2195.        return;
  2196.     } else if (ix == ox) {
  2197.        /* vertical line */
  2198.        /* assume inrange(ix, x_min, x_max) */
  2199.        *ex = ix;        /* == ox */
  2200.  
  2201.        if (inrange(y_max, iy, oy))
  2202.         *ey = y_max;
  2203.        else if (inrange(y_min, iy, oy))
  2204.         *ey = y_min;
  2205.        else {
  2206.         graph_error("error in edge_intersect");
  2207.        }
  2208.        return;
  2209.     }
  2210.  
  2211.     /* slanted line of some kind */
  2212.  
  2213.     /* does it intersect y_min edge */
  2214.     if (inrange(y_min, iy, oy) && y_min != iy && y_min != oy) {
  2215.        x = ix + (y_min-iy) * ((ox-ix) / (oy-iy));
  2216.        if (inrange(x, x_min, x_max)) {
  2217.           *ex = x;
  2218.           *ey = y_min;
  2219.           return;            /* yes */
  2220.        }
  2221.     }
  2222.     
  2223.     /* does it intersect y_max edge */
  2224.     if (inrange(y_max, iy, oy) && y_max != iy && y_max != oy) {
  2225.        x = ix + (y_max-iy) * ((ox-ix) / (oy-iy));
  2226.        if (inrange(x, x_min, x_max)) {
  2227.           *ex = x;
  2228.           *ey = y_max;
  2229.           return;            /* yes */
  2230.        }
  2231.     }
  2232.  
  2233.     /* does it intersect x_min edge */
  2234.     if (inrange(x_min, ix, ox) && x_min != ix && x_min != ox) {
  2235.        y = iy + (x_min-ix) * ((oy-iy) / (ox-ix));
  2236.        if (inrange(y, y_min, y_max)) {
  2237.           *ex = x_min;
  2238.           *ey = y;
  2239.           return;
  2240.        }
  2241.     }
  2242.  
  2243.     /* does it intersect x_max edge */
  2244.     if (inrange(x_max, ix, ox) && x_max != ix && x_max != ox) {
  2245.        y = iy + (x_max-ix) * ((oy-iy) / (ox-ix));
  2246.        if (inrange(y, y_min, y_max)) {
  2247.           *ex = x_max;
  2248.           *ey = y;
  2249.           return;
  2250.        }
  2251.     }
  2252.  
  2253.     /* If we reach here, the inrange point is on the edge, and
  2254.      * the line segment from the outrange point does not cross any 
  2255.      * other edges to get there. In this case, we return the inrange 
  2256.      * point as the 'edge' intersection point. This will basically draw
  2257.      * line.
  2258.      */
  2259.     *ex = ix; 
  2260.     *ey = iy;
  2261.     return;
  2262. }
  2263.  
  2264. /* XXX - JG  */
  2265. /* single edge intersection algorithm for "steps" curves */
  2266. /* 
  2267.  * Given two points, one inside and one outside the plot, return
  2268.  * the point where an edge of the plot intersects the line segments
  2269.  * forming the step between the two points. 
  2270.  *
  2271.  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from  
  2272.  * P1 to P2 is drawn as two line segments: (x1,y1)->(x2,y1) and 
  2273.  * (x2,y1)->(x2,y2). 
  2274.  */
  2275. static void
  2276. edge_intersect_steps(points, i, ex, ey)
  2277.     struct coordinate GPHUGE *points; /* the points array */
  2278.     int i;                /* line segment from point i-1 to point i */
  2279.     double *ex, *ey;        /* the point where it crosses an edge */
  2280. {
  2281.     /* global x_min, x_max, y_min, x_max */
  2282.     double ax = points[i-1].x;
  2283.     double ay = points[i-1].y;
  2284.     double bx = points[i].x;
  2285.     double by = points[i].y;
  2286.  
  2287.     if (points[i].type == INRANGE) {    /* from OUTRANGE to INRANG */
  2288.         if (inrange(ay,y_min,y_max)) {
  2289.         *ey = ay;
  2290.         if (ax > x_max)
  2291.             *ex = x_max;
  2292.         else            /* x < x_min */
  2293.             *ex = x_min;
  2294.         } else {
  2295.             *ex = bx;
  2296.         if (ay > y_max)     
  2297.             *ey = y_max;
  2298.         else            /* y < y_min */
  2299.             *ey = y_min;
  2300.         }
  2301.     } else {                /* from INRANGE to OUTRANGE */
  2302.         if (inrange(bx,x_min,x_max)) {
  2303.         *ex = bx;
  2304.         if (by > y_max)
  2305.             *ey = y_max;
  2306.         else            /* y < y_min */
  2307.             *ey = y_min;
  2308.         } else {
  2309.             *ey = ay;
  2310.         if (bx > x_max)     
  2311.             *ex = x_max;
  2312.         else            /* x < x_min */
  2313.             *ex = x_min;
  2314.         }
  2315.     }
  2316.     return;
  2317. }
  2318.  
  2319. /* XXX - HOE  */
  2320. /* single edge intersection algorithm for "fsteps" curves */
  2321. /* fsteps means step on forward y-value. 
  2322.  * Given two points, one inside and one outside the plot, return
  2323.  * the point where an edge of the plot intersects the line segments
  2324.  * forming the step between the two points. 
  2325.  *
  2326.  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from  
  2327.  * P1 to P2 is drawn as two line segments: (x1,y1)->(x1,y2) and 
  2328.  * (x1,y2)->(x2,y2). 
  2329.  */
  2330. static void
  2331. edge_intersect_fsteps(points, i, ex, ey)
  2332.     struct coordinate GPHUGE *points; /* the points array */
  2333.     int i;                /* line segment from point i-1 to point i */
  2334.     double *ex, *ey;        /* the point where it crosses an edge */
  2335. {
  2336.     /* global x_min, x_max, y_min, x_max */
  2337.     double ax = points[i-1].x;
  2338.     double ay = points[i-1].y;
  2339.     double bx = points[i].x;
  2340.     double by = points[i].y;
  2341.  
  2342.     if (points[i].type == INRANGE) {    /* from OUTRANGE to INRANG */
  2343.         if (inrange(ax,x_min,x_max)) {
  2344.         *ex = ax;
  2345.         if (ay > y_max)
  2346.             *ey = y_max;
  2347.         else            /* y < y_min */
  2348.             *ey = y_min;
  2349.         } else {
  2350.             *ey = by;
  2351.         if (bx > x_max)     
  2352.             *ex = x_max;
  2353.         else            /* x < x_min */
  2354.             *ex = x_min;
  2355.         }
  2356.     } else {                /* from INRANGE to OUTRANGE */
  2357.         if (inrange(by,y_min,y_max)) {
  2358.         *ey = by;
  2359.         if (bx > x_max)
  2360.             *ex = x_max;
  2361.         else            /* x < x_min */
  2362.             *ex = x_min;
  2363.         } else {
  2364.             *ex = ax;
  2365.         if (by > y_max)     
  2366.             *ey = y_max;
  2367.         else            /* y < y_min */
  2368.             *ey = y_min;
  2369.         }
  2370.     }
  2371.     return;
  2372. }
  2373.  
  2374. /* XXX - JG  */
  2375. /* double edge intersection algorithm for "steps" plot */
  2376. /* Given two points, both outside the plot, return the points where an 
  2377.  * edge of the plot intersects the line segments forming a step 
  2378.  * by the two points. There may be zero, one, two, or an infinite number
  2379.  * of intersection points. (One means an intersection at a corner, infinite
  2380.  * means overlaying the edge itself). We return FALSE when there is nothing
  2381.  * to draw (zero intersections), and TRUE when there is something to 
  2382.  * draw (the one-point case is a degenerate of the two-point case and we do 
  2383.  * not distinguish it - we draw it anyway).
  2384.  *
  2385.  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from  
  2386.  * P1 to P2 is drawn as two line segments: (x1,y1)->(x2,y1) and 
  2387.  * (x2,y1)->(x2,y2). 
  2388.  */
  2389. static TBOOLEAN                /* any intersection? */
  2390. two_edge_intersect_steps(points, i, lx, ly)
  2391.     struct coordinate GPHUGE *points; /* the points array */
  2392.     int i;                /* line segment from point i-1 to point i */
  2393.     double *lx, *ly;        /* lx[2], ly[2]: points where it crosses edges */
  2394. {
  2395.     /* global x_min, x_max, y_min, x_max */
  2396.     double ax = points[i-1].x;
  2397.     double ay = points[i-1].y;
  2398.     double bx = points[i].x;
  2399.     double by = points[i].y;
  2400.  
  2401.     if ( max(ax,bx) < x_min || min(ax,bx) > x_max || 
  2402.          max(ay,by) < y_min || min(ay,by) > y_max ||
  2403.          ( (ay  > y_max || ay < y_min)            &&
  2404.            (bx  > x_max || bx < x_min)  ) ) {
  2405.     return(FALSE);                
  2406.     } else if (inrange(ay,y_min,y_max) && inrange(bx,x_min,x_max)) {    /* corner of step inside plotspace */
  2407.         *ly++ = ay;
  2408.     if (ax < x_min) 
  2409.         *lx++ = x_min;
  2410.     else 
  2411.         *lx++ = x_max;
  2412.  
  2413.     *lx++ = bx;
  2414.     if (by < y_min) 
  2415.         *ly++ = y_min;
  2416.     else 
  2417.         *ly++ = y_max;
  2418.  
  2419.     return(TRUE);
  2420.     } else if (inrange(ay,y_min,y_max)) {    /* cross plotspace in x-direction */
  2421.     *lx++ = x_min;
  2422.     *ly++ = ay;
  2423.     *lx++ = x_max;
  2424.     *ly++ = ay;
  2425.     return(TRUE);
  2426.     } else if (inrange(ax,x_min,x_max)) {    /* cross plotspace in y-direction */
  2427.     *lx++ = bx;
  2428.     *ly++ = y_min;
  2429.     *lx++ = bx;
  2430.     *ly++ = y_max;
  2431.     return(TRUE);
  2432.     } else
  2433.     return(FALSE);
  2434. }
  2435.  
  2436. /* XXX - HOE  */
  2437. /* double edge intersection algorithm for "fsteps" plot */
  2438. /* Given two points, both outside the plot, return the points where an 
  2439.  * edge of the plot intersects the line segments forming a step 
  2440.  * by the two points. There may be zero, one, two, or an infinite number
  2441.  * of intersection points. (One means an intersection at a corner, infinite
  2442.  * means overlaying the edge itself). We return FALSE when there is nothing
  2443.  * to draw (zero intersections), and TRUE when there is something to 
  2444.  * draw (the one-point case is a degenerate of the two-point case and we do 
  2445.  * not distinguish it - we draw it anyway).
  2446.  *
  2447.  * Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from  
  2448.  * P1 to P2 is drawn as two line segments: (x1,y1)->(x1,y2) and 
  2449.  * (x1,y2)->(x2,y2). 
  2450.  */
  2451. static TBOOLEAN                /* any intersection? */
  2452. two_edge_intersect_fsteps(points, i, lx, ly)
  2453.     struct coordinate GPHUGE *points; /* the points array */
  2454.     int i;        /* line segment from point i-1 to point i */
  2455.     double *lx, *ly; /* lx[2], ly[2]: points where it crosses edges */
  2456. {
  2457.     /* global x_min, x_max, y_min, x_max */
  2458.     double ax = points[i-1].x;
  2459.     double ay = points[i-1].y;
  2460.     double bx = points[i].x;
  2461.     double by = points[i].y;
  2462.  
  2463.     if ( max(ax,bx) < x_min || min(ax,bx) > x_max || 
  2464.          max(ay,by) < y_min || min(ay,by) > y_max ||
  2465.          ( (by  > y_max || by < y_min)            &&
  2466.            (ax  > x_max || ax < x_min)  ) ) {
  2467.     return(FALSE);                
  2468.     } else if (inrange(by,y_min,y_max) && inrange(ax,x_min,x_max)) {    /* corner of step inside plotspace */
  2469.         *lx++ = ax;
  2470.     if (ay < y_min) 
  2471.         *ly++ = y_min;
  2472.     else 
  2473.         *ly++ = y_max;
  2474.  
  2475.     *ly = by;
  2476.     if (bx < x_min) 
  2477.         *lx = x_min;
  2478.     else 
  2479.         *lx = x_max;
  2480.  
  2481.     return(TRUE);
  2482.     } else if (inrange(by,y_min,y_max)) {    /* cross plotspace in x-direction */
  2483.     *lx++ = x_min;
  2484.     *ly++ = by;
  2485.     *lx = x_max;
  2486.     *ly = by;
  2487.     return(TRUE);
  2488.     } else if (inrange(ax,x_min,x_max)) {    /* cross plotspace in y-direction */
  2489.     *lx++ = ax;
  2490.     *ly++ = y_min;
  2491.     *lx = ax;
  2492.     *ly = y_max;
  2493.     return(TRUE);
  2494.     } else
  2495.     return(FALSE);
  2496. }
  2497.  
  2498. /* double edge intersection algorithm */
  2499. /* Given two points, both outside the plot, return
  2500.  * the points where an edge of the plot intersects the line segment defined 
  2501.  * by the two points. There may be zero, one, two, or an infinite number
  2502.  * of intersection points. (One means an intersection at a corner, infinite
  2503.  * means overlaying the edge itself). We return FALSE when there is nothing
  2504.  * to draw (zero intersections), and TRUE when there is something to 
  2505.  * draw (the one-point case is a degenerate of the two-point case and we do 
  2506.  * not distinguish it - we draw it anyway).
  2507.  */
  2508. static TBOOLEAN                /* any intersection? */
  2509. two_edge_intersect(points, i, lx, ly)
  2510.     struct coordinate GPHUGE *points; /* the points array */
  2511.     int i;                /* line segment from point i-1 to point i */
  2512.     double *lx, *ly;        /* lx[2], ly[2]: points where it crosses edges */
  2513. {
  2514.     /* global x_min, x_max, y_min, x_max */
  2515.     int count;
  2516.     double ix = points[i-1].x;
  2517.     double iy = points[i-1].y;
  2518.     double ox = points[i].x;
  2519.     double oy = points[i].y;
  2520.     double t[4];
  2521.     double swap;
  2522.     double t_min, t_max;
  2523. #if 0
  2524.     fprintf(stderr, "\ntwo_edge_intersect (%g, %g) and (%g, %g) : ",
  2525.         points[i-1].x, points[i-1].y,
  2526.         points[i].x, points[i].y);
  2527. #endif
  2528.     /* nasty degenerate cases, effectively drawing to an infinity point (?)
  2529.        cope with them here, so don't process them as a "real" OUTRANGE point 
  2530.  
  2531.        If more than one coord is -VERYLARGE, then can't ratio the "infinities"
  2532.        so drop out by returning FALSE */
  2533.     
  2534.     count = 0;
  2535.     if(ix == -VERYLARGE) count++;
  2536.     if(ox == -VERYLARGE) count++;
  2537.     if(iy == -VERYLARGE) count++;
  2538.     if(oy == -VERYLARGE) count++;
  2539.  
  2540.     /* either doesn't pass through graph area *or* 
  2541.        can't ratio infinities to get a direction to draw line, so simply return(FALSE) */
  2542.     if(count > 1){
  2543. #if 0
  2544.       fprintf(stderr, "\tA\n");
  2545. #endif
  2546.       return(FALSE);
  2547.     }
  2548.  
  2549.     if(ox == -VERYLARGE || ix == -VERYLARGE)
  2550.       {
  2551.     if(ix == -VERYLARGE)
  2552.       {
  2553.         /* swap points so ix/iy don't have a -VERYLARGE component */
  2554.         swap = ix;ix = ox;ox = swap;
  2555.         swap = iy;iy = oy;oy = swap;
  2556.       }
  2557.  
  2558.     /* check actually passes through the graph area */
  2559.     if(ix > x_max && inrange(iy, y_min, y_max))
  2560.       {
  2561.         lx[0] = x_min;
  2562.         ly[0] = iy;
  2563.  
  2564.         lx[1] = x_max;
  2565.         ly[1] = iy;
  2566. #if 0
  2567.     fprintf(stderr, "(%g %g) -> (%g %g)", 
  2568.         lx[0], ly[0], lx[1], ly[1]);
  2569. #endif
  2570.         return(TRUE);
  2571.       }
  2572.     else {
  2573. #if 0
  2574.       fprintf(stderr, "\tB\n");
  2575. #endif
  2576.       return(FALSE);
  2577.     }
  2578.       }
  2579.  
  2580.     if(oy == -VERYLARGE || iy == -VERYLARGE)
  2581.       {
  2582.     if(iy == -VERYLARGE)
  2583.       {
  2584.         /* swap points so ix/iy don't have a -VERYLARGE component */
  2585.         swap = ix;ix = ox;ox = swap;
  2586.         swap = iy;iy = oy;oy = swap;
  2587.       }
  2588.  
  2589.     /* check actually passes through the graph area */
  2590.     if(iy > y_max && inrange(ix, x_min, x_max))
  2591.       {
  2592.         lx[0] = ix;
  2593.         ly[0] = y_min;
  2594.  
  2595.         lx[1] = ix;
  2596.         ly[1] = y_max;
  2597. #if 0
  2598.     fprintf(stderr, "(%g %g) -> (%g %g)", 
  2599.         lx[0], ly[0], lx[1], ly[1]);
  2600. #endif
  2601.         return(TRUE);
  2602.       }
  2603.     else {
  2604. #if 0
  2605.       fprintf(stderr, "\tC\n");
  2606. #endif
  2607.       return(FALSE);
  2608.     }
  2609.       }
  2610.  
  2611.     /*
  2612.      * Special horizontal/vertical, etc. cases are checked and remaining
  2613.      * slant lines are checked separately.
  2614.      *
  2615.      * The slant line intersections are solved using the parametric form
  2616.      * of the equation for a line, since if we test x/y min/max planes explicitly
  2617.      * then e.g. a  line passing through a corner point (x_min,y_min) 
  2618.      * actually intersects 2 planes and hence further tests would be required 
  2619.      * to anticipate this and similar situations.
  2620.      */
  2621.  
  2622.     /*
  2623.      * Can have case (ix == ox && iy == oy) as both points OUTRANGE
  2624.      */
  2625.     if(ix == ox && iy == oy)
  2626.       {
  2627.     /* but as only define single outrange point, can't intersect graph area */
  2628.     return(FALSE);
  2629.       }
  2630.  
  2631.     if(ix == ox) {
  2632.     /* line parallel to y axis */
  2633.     
  2634.     /* x coord must be in range, and line must span both y_min and y_max */
  2635.     /* note that spanning y_min implies spanning y_max, as both points OUTRANGE */
  2636.     if(!inrange(ix, x_min, x_max))
  2637.       {
  2638.         return(FALSE);
  2639.       }
  2640.  
  2641.     if (inrange(y_min, iy, oy)) {
  2642.       lx[0] = ix;
  2643.       ly[0] = y_min;
  2644.  
  2645.       lx[1] = ix;
  2646.       ly[1] = y_max;
  2647. #if 0
  2648.       fprintf(stderr, "(%g %g) -> (%g %g)", 
  2649.           lx[0], ly[0], lx[1], ly[1]);
  2650. #endif
  2651.       return(TRUE);
  2652.     } else
  2653.       return(FALSE);
  2654.       }
  2655.  
  2656.     if(iy == oy) {
  2657.       /* already checked case (ix == ox && iy == oy) */
  2658.       
  2659.       /* line parallel to x axis */
  2660.     /* y coord must be in range, and line must span both x_min and x_max */
  2661.     /* note that spanning x_min implies spanning x_max, as both points OUTRANGE */
  2662.     if(!inrange(iy, y_min, y_max))
  2663.       {
  2664.         return(FALSE);
  2665.       }
  2666.  
  2667.     if (inrange(x_min, ix, ox)) {
  2668.       lx[0] = x_min;
  2669.       ly[0] = iy;
  2670.  
  2671.       lx[1] = x_max;
  2672.       ly[1] = iy;
  2673. #if 0
  2674.     fprintf(stderr, "(%g %g) -> (%g %g)", 
  2675.         lx[0], ly[0], lx[1], ly[1]);
  2676. #endif
  2677.       return(TRUE);
  2678.     } else
  2679.       return(FALSE);
  2680.       }
  2681.  
  2682.     /* nasty 2D slanted line in an xy plane */
  2683.  
  2684.     /*
  2685.       Solve parametric equation
  2686.       
  2687.       (ix, iy) + t (diff_x, diff_y)
  2688.  
  2689.       where 0.0 <= t <= 1.0 and
  2690.  
  2691.       diff_x = (ox - ix);
  2692.       diff_y = (oy - iy);
  2693.       */
  2694.  
  2695.       t[0] = (x_min - ix)/(ox - ix);
  2696.       t[1] = (x_max - ix)/(ox - ix);
  2697.       
  2698.       if(t[0] > t[1]) {
  2699.     swap = t[0];t[0] = t[1];t[1] = swap;
  2700.       }
  2701.  
  2702.       t[2] = (y_min - iy)/(oy - iy);
  2703.       t[3] = (y_max - iy)/(oy - iy);
  2704.       
  2705.       if(t[2] > t[3]) {
  2706.     swap = t[2];t[2] = t[3];t[3] = swap;
  2707.       }
  2708.  
  2709.       t_min = max(max(t[0],t[2]),0.0);
  2710.       t_max = min(min(t[1],t[3]),1.0);
  2711.       
  2712.       if(t_min > t_max)
  2713.     return(FALSE);
  2714.  
  2715.       lx[0] = ix + t_min * (ox - ix);
  2716.       ly[0] = iy + t_min * (oy - iy);
  2717.       
  2718.       lx[1] = ix + t_max * (ox - ix);
  2719.       ly[1] = iy + t_max * (oy - iy);
  2720.       
  2721.       /*
  2722.        * Can only have 0 or 2 intersection points -- only need test one coord
  2723.        */
  2724.       if(inrange(lx[0], x_min, x_max) && 
  2725.      inrange(ly[0], y_min, y_max))
  2726.     {
  2727. #if 0
  2728.       fprintf(stderr, "(%g %g) -> (%g %g)", 
  2729.           lx[0], ly[0], lx[1], ly[1]);
  2730. #endif
  2731.       return(TRUE);
  2732.     }
  2733.       
  2734.     return(FALSE);
  2735. }
  2736.  
  2737. /* justify ticplace to a proper date-time value */
  2738. double
  2739. time_tic_just(level,ticplace)
  2740. int level;
  2741. double ticplace;
  2742. {
  2743.     struct tm tm;
  2744.  
  2745.     if (level <= 0) {
  2746.         return(ticplace);
  2747.     }
  2748.     ggmtime(&tm,(double)ticplace);
  2749.       if (level > 0) { /* units of minutes */
  2750.           if ( tm.tm_sec > 50 ) 
  2751.               tm.tm_min ++;
  2752.           tm.tm_sec = 0;
  2753.       }
  2754.       if (level > 1) { /* units of hours */
  2755.           if ( tm.tm_min > 50 )
  2756.               tm.tm_hour ++;
  2757.           tm.tm_min = 0;
  2758.       }
  2759.       if (level > 2) { /* units of days */
  2760.         if ( tm.tm_hour > 14 ) {
  2761.             tm.tm_hour = 0;
  2762.             tm.tm_mday = 0;
  2763.               tm.tm_yday ++;
  2764.              ggmtime(&tm,(double)gtimegm(&tm));
  2765.           } else if (tm.tm_hour > 7 ) {
  2766.               tm.tm_hour = 12;
  2767.           } else if (tm.tm_hour > 3 ) {
  2768.               tm.tm_hour = 6;
  2769.           } else {
  2770.               tm.tm_hour = 0;
  2771.           }
  2772.       }
  2773.       /* skip it, I have not bothered with weekday so far */
  2774.     if (level > 4) { /* units of month */
  2775.         if ( tm.tm_mday > 25 ) {
  2776.             tm.tm_mon ++;
  2777.             if (tm.tm_mon > 11) {
  2778.                 tm.tm_year++;
  2779.                 tm.tm_mon = 0;
  2780.             }
  2781.         }
  2782.         tm.tm_mday = 1;
  2783.     }
  2784.     if (level > 5) {
  2785.         if ( tm.tm_mon >= 7 )
  2786.             tm.tm_year ++;
  2787.         tm.tm_mon = 0;
  2788.     }
  2789.     ticplace = (double) gtimegm(&tm);
  2790.     ggmtime(&tm,(double)gtimegm(&tm));
  2791.     return(ticplace);
  2792. }
  2793.  
  2794. /* make smalltics for time-axis */
  2795. double
  2796. make_ltic(tlevel,incr)
  2797. int tlevel;
  2798. double incr;
  2799. {
  2800.     double tinc;
  2801.     tinc = 0;
  2802.     if ( tlevel < 0 ) tlevel = 0;
  2803.     switch ( tlevel ) {
  2804.         case 0: 
  2805.         case 1: {
  2806.             if ( incr >= 20 )
  2807.                 tinc = 10;
  2808.             if ( incr >= 60 )
  2809.                 tinc = 30;
  2810.             if ( incr >= 2*60 )
  2811.                 tinc = 60;
  2812.             if ( incr >= 5*60 )
  2813.                 tinc = 2*60;
  2814.             if ( incr >= 10*60 )
  2815.                 tinc = 5*60;
  2816.             if ( incr >= 20*60 )
  2817.                 tinc = 10*60;
  2818.             break;
  2819.             }
  2820.         case 2: {
  2821.             if ( incr >= 20*60 )
  2822.                 tinc = 10*60;
  2823.             if ( incr >= 3600 )
  2824.                 tinc = 30*60;
  2825.             if ( incr >= 2*3600 )
  2826.                 tinc = 3600;
  2827.             if ( incr >= 5*3600 )
  2828.                 tinc = 2*3600;
  2829.             if ( incr >= 10*3600 )
  2830.                 tinc = 5*3600;
  2831.             if ( incr >= 20*3600 )
  2832.                 tinc = 10*3600;
  2833.             break;
  2834.             }
  2835.         case 3: {
  2836.             if ( incr > 2*3600 )
  2837.                 tinc = 3600;
  2838.             if ( incr > 4*3600 )
  2839.                 tinc = 2*3600;
  2840.             if ( incr > 7*3600 )
  2841.                 tinc = 3*3600;
  2842.             if ( incr > 13*3600 )
  2843.                 tinc = 6*3600;
  2844.             if ( incr > DAY_SEC )
  2845.                 tinc = 12*3600;
  2846.             if ( incr > 2*DAY_SEC )
  2847.                 tinc = DAY_SEC;
  2848.             break;
  2849.             }
  2850.         case 4: { /* week: tic per day */
  2851.             if ( incr > 2*DAY_SEC )
  2852.                 tinc = DAY_SEC;
  2853.             if ( incr > 7*DAY_SEC )
  2854.                 tinc = 7*DAY_SEC;
  2855.             break;
  2856.             }
  2857.         case 5: { /* month */
  2858.             if ( incr > 2*DAY_SEC )
  2859.                 tinc = DAY_SEC;
  2860.             if ( incr > 15*DAY_SEC )
  2861.                 tinc = 10*DAY_SEC;
  2862.             if ( incr > 2*MON_SEC )
  2863.                 tinc = MON_SEC;
  2864.             if ( incr > 6*MON_SEC )
  2865.                 tinc = 3*MON_SEC;
  2866.             if ( incr > 2*YEAR_SEC )
  2867.                 tinc = YEAR_SEC;
  2868.             break;
  2869.             }
  2870.         case 6: { /* year */
  2871.             if ( incr > 2*MON_SEC )
  2872.                 tinc = MON_SEC;
  2873.             if ( incr > 6*MON_SEC )
  2874.                 tinc = 3*MON_SEC;
  2875.             if ( incr > 2*YEAR_SEC )
  2876.                 tinc = YEAR_SEC;
  2877.             if ( incr > 10*YEAR_SEC )
  2878.                 tinc = 5*YEAR_SEC;
  2879.             if ( incr > 50*YEAR_SEC ) 
  2880.                 tinc = 10*YEAR_SEC;
  2881.             if ( incr > 100*YEAR_SEC ) 
  2882.                 tinc = 20*YEAR_SEC;
  2883.             if ( incr > 200*YEAR_SEC ) 
  2884.                 tinc = 50*YEAR_SEC;
  2885.             if ( incr > 300*YEAR_SEC ) 
  2886.                 tinc = 100*YEAR_SEC;
  2887.             break;
  2888.             }
  2889.     }
  2890.     return(tinc);
  2891. }
  2892.  
  2893.  
  2894. void write_multiline(x,y,text,hor,vert,angle, font)
  2895. unsigned int x,y;
  2896. char *text;
  2897. int hor,vert; /* horizontal and vertical just - text in hor direction despite angle */
  2898. int angle; /* assume term has already been set for this */
  2899. char *font; /* NULL or "" means use default */
  2900. {
  2901.     /* assumes we are free to mangle the text */
  2902.     register struct termentry *t = term;
  2903.     char *p;
  2904.     if (vert != JUST_TOP) {
  2905.         /* count lines and adjust y */
  2906.         int lines=0; /* number of linefeeds - one fewer than lines */
  2907.         for (p=text; *p; ++p)
  2908.             if (*p=='\n') ++lines;
  2909.         if (angle)
  2910.             x -= (vert*lines*t->v_char)/2;
  2911.         else
  2912.             y += (vert*lines*t->v_char)/2;
  2913.     }
  2914.  
  2915.     if (font && *font)
  2916.         (*t->set_font)(font);
  2917.         
  2918.  
  2919.     for(;;) { /* we will explicitly break out */
  2920.     
  2921.         if ( (p=strchr(text, '\n')) != NULL )
  2922.             *p=0; /* terminate the string */
  2923.  
  2924.         if ((*t->justify_text)(hor)) {
  2925.             (*t->put_text)(x, y, text);
  2926.         } else {
  2927.             int fix=hor*(t->h_char)*strlen(text)/2;
  2928.             if (angle)
  2929.                 (*t->put_text)(x, y-fix, text);
  2930.             else
  2931.                 (*t->put_text)(x - fix, y, text);
  2932.         }
  2933.         if (angle)
  2934.             x += t->v_char;
  2935.         else
  2936.             y -= t->v_char;
  2937.  
  2938.         if (!p)
  2939.             break;
  2940.  
  2941.         text = p+1;
  2942.     } /* unconditional branch back to the for(;;) - just a goto ! */
  2943.  
  2944.     if (font && *font)
  2945.         (*t->set_font)(default_font);
  2946. }
  2947.  
  2948. /* display a x-axis ticmark - called by gen_ticks */
  2949. /* also uses global tic_start, tic_direction, tic_text and tic_just */
  2950. static void xtick2d_callback(axis, place, text, grid)
  2951. int axis;
  2952. double place;
  2953. char *text;
  2954. int grid; /* linetype or -2 for no grid */
  2955. {
  2956.     register struct termentry *t = term;
  2957.     /* minitick if text is NULL - beware - h_tic is unsigned */
  2958.     int ticsize = tic_direction*(int)(t->v_tic)*(text ? ticscale : miniticscale);
  2959.     unsigned int x=map_x(place);
  2960.  
  2961.   if (grid > -2) {
  2962.     (*t->linetype)(grid);
  2963.     if (polar_grid_angle) {
  2964.     double x=place, y=0, s=sin(0.1), c=cos(0.1);
  2965.     int i;
  2966.     if (place > largest_polar_circle) largest_polar_circle=place;
  2967.     else if (-place > largest_polar_circle) largest_polar_circle=-place;
  2968.     clip_move(map_x(x),map_y(0));
  2969.     for (i=1;i<=63 /* 2pi/0.1*/; ++i) {
  2970.         {
  2971.             /* cos(t+dt)=cos(t)cos(dt)-sin(t)cos(dt) */
  2972.             double tx=x*c-y*s;
  2973.             /* sin(t+dt)=sin(t)cos(dt)+cos(t)sin(dt) */
  2974.             y=y*c+x*s;
  2975.             x=tx;
  2976.         }
  2977.         clip_vector(map_x(x), map_y(y));
  2978.     }
  2979.     } else {
  2980.         if (x < key_xr && x > key_xl ) {
  2981.         if ( key_yb > ybot ) {
  2982.              (*t->move)(x, ybot);
  2983.              (*t->vector)(x, key_yb);
  2984.         }
  2985.         if ( key_yt < ytop ) {
  2986.              (*t->move)(x,key_yt);
  2987.              (*t->vector)(x, ytop);
  2988.         }
  2989.         } else {
  2990.             (*t->move)(x, ybot);
  2991.             (*t->vector)(x, ytop);
  2992.         }
  2993.     }
  2994.     (*t->linetype)(-2); /* border linetype */
  2995.   }
  2996.  
  2997.     /* we precomputed tic posn and text posn in global vars */
  2998.  
  2999.     (*t->move)(x, tic_start);
  3000.     (*t->vector)(x,tic_start+ticsize);
  3001.  
  3002.     if (tic_mirror >= 0) {
  3003.     (*t->move)(x, tic_mirror);
  3004.     (*t->vector)(x,tic_mirror-ticsize);
  3005.     }
  3006.      
  3007.     if (text) {
  3008.         write_multiline(x, tic_text, text, CENTRE, tic_just, 0, NULL);
  3009.     }
  3010. }
  3011.  
  3012. /* display a y-axis ticmark - called by gen_ticks */
  3013. /* also uses global tic_start, tic_direction, tic_text and tic_just */
  3014. static void ytick2d_callback(axis, place, text, grid)
  3015. int axis;
  3016. double place;
  3017. char *text;
  3018. int grid; /* linetype or -2 */
  3019. {
  3020.     register struct termentry *t = term;
  3021.     /* minitick if text is NULL - v_tic is unsigned */
  3022.     int ticsize = tic_direction*(int)(t->h_tic)*(text ? ticscale : miniticscale);
  3023.     unsigned int y=map_y(place);
  3024.   if (grid > -2) {
  3025.     (*t->linetype)(grid);
  3026.     if (polar_grid_angle) {
  3027.     double x=0, y=place, s=sin(0.1), c=cos(0.1);
  3028.     int i;
  3029.     if (place > largest_polar_circle) largest_polar_circle=place;
  3030.     else if (-place > largest_polar_circle) largest_polar_circle=-place;
  3031.     clip_move(map_x(x),map_y(y));
  3032.     for (i=1;i<=63 /* 2pi/0.1*/; ++i) {
  3033.         {
  3034.             /* cos(t+dt)=cos(t)cos(dt)-sin(t)cos(dt) */
  3035.             double tx=x*c-y*s;
  3036.             /* sin(t+dt)=sin(t)cos(dt)+cos(t)sin(dt) */
  3037.             y=y*c+x*s;
  3038.             x=tx;
  3039.         }
  3040.         clip_vector( map_x(x), map_y(y) );
  3041.     }
  3042.     } else {
  3043.          if (y < key_yt && y > key_yb && key_xl < xright /* catch TOUT */ ) {
  3044.         if ( key_xl > xleft ) {
  3045.              (*t->move)(xleft, y);
  3046.              (*t->vector)(key_xl, y);
  3047.         }
  3048.         if ( key_xr < xright ) {
  3049.              (*t->move)(key_xr, y);
  3050.              (*t->vector)(xright, y);
  3051.         }
  3052.          } else {
  3053.             (*t->move)(xleft, y);
  3054.             (*t->vector)(xright, y);
  3055.          }
  3056.     }
  3057.     (*t->linetype)(-2); /* border linetype */
  3058.   }
  3059.   
  3060.     /* we precomputed tic posn and text posn */
  3061.  
  3062.     (*t->move)(tic_start, y);
  3063.     (*t->vector)(tic_start+ticsize, y);
  3064.  
  3065.     if (tic_mirror >= 0) {
  3066.     (*t->move)(tic_mirror, y);
  3067.     (*t->vector)(tic_mirror-ticsize, y);
  3068.     }
  3069.     
  3070.     if (text) {
  3071.         write_multiline(tic_text, y, text, tic_just, JUST_CENTRE, 0, NULL);
  3072.     }
  3073. }
  3074.  
  3075. int 
  3076. label_width(str,lines)
  3077. char *str;
  3078. int *lines;
  3079. {
  3080.     char lab[MAX_LINE_LEN+1], *s, *e;
  3081.     int mlen, len, l;
  3082.  
  3083.     l = mlen = len = 0;
  3084.     sprintf(lab,"%s\n",str);
  3085.     s = lab;
  3086.     while( (e=(char *)strchr(s,'\n')) ) {
  3087.         *e = '\0';
  3088.         len = strlen(s); /* = e-s ? */
  3089.         if ( len > mlen ) mlen = len;
  3090.         if ( len || l ) l++;
  3091.         s = ++e;
  3092.     }
  3093.     /* lines=NULL => not interested - div */
  3094.     if (lines) *lines = l;
  3095.     return(mlen);
  3096. }
  3097.  
  3098.  
  3099. void setup_tics(axis,ticdef, format, max)
  3100. int axis;
  3101. struct ticdef *ticdef;
  3102. char *format;
  3103. int max; /* approx max number of slots available */
  3104. {
  3105.     double tic;
  3106.  
  3107.     int fixmin=(auto_array[axis] & 1) != 0;
  3108.     int fixmax=(auto_array[axis] & 2) != 0;
  3109.  
  3110.     if (ticdef->type == TIC_SERIES) {
  3111.         ticstep[axis] = tic = ticdef->def.series.incr;
  3112.         fixmin &=  (ticdef->def.series.start == -VERYLARGE);
  3113.         fixmax &=  (ticdef->def.series.end   ==  VERYLARGE);
  3114.     } else if (ticdef->type == TIC_COMPUTED) {
  3115.         ticstep[axis] = tic = make_tics(axis, max);
  3116.     }
  3117.  
  3118.     if (fixmin) {
  3119.         if (min_array[axis] < max_array[axis])
  3120.             min_array[axis] = tic * floor(min_array[axis]/tic);
  3121.         else
  3122.             min_array[axis] = tic * ceil(min_array[axis]/tic);
  3123.     }
  3124.  
  3125.     if (fixmax) {
  3126.         if (min_array[axis] < max_array[axis])
  3127.             max_array[axis] = tic * ceil(max_array[axis]/tic);
  3128.         else
  3129.             max_array[axis] = tic * floor(max_array[axis]/tic);
  3130.     }
  3131.             
  3132.     if (datatype[axis] == TIME && format_is_numeric[axis])
  3133.         /* invent one for them */
  3134.         timetic_format(axis, min_array[axis],max_array[axis]);
  3135.     else
  3136.         strcpy(ticfmt[axis], format);
  3137. }
  3138.  
  3139. /*{{{  mant_exp - split into mantissa and/or exponent*/
  3140. static void mant_exp(log_base, x, scientific, m,p)
  3141. double log_base, x;
  3142. int scientific; /* round to power of 3 */
  3143. double *m;
  3144. int *p; /* results */
  3145. {
  3146.     int sign=1;
  3147.     double l10;
  3148.     int power;
  3149.     /*{{{  check 0*/
  3150.     if (x==0) {
  3151.         if (m) *m=0;
  3152.         if (p) *p=0;
  3153.         return;
  3154.     }
  3155.     /*}}}*/
  3156.     /*{{{  check -ve*/
  3157.     if (x<0) {
  3158.         sign=(-1);
  3159.         x=(-x);
  3160.     }
  3161.     /*}}}*/
  3162.  
  3163.     l10=log10(x) / log_base;
  3164.     power = floor(l10);
  3165.     if (scientific) {
  3166.         power = 3*(power/3);
  3167.     }
  3168.  
  3169.     if (m) *m = sign * pow( 10.0, (l10-power)*log_base );
  3170.     if (p) *p = power;
  3171. }
  3172. /*}}}*/
  3173.     
  3174.  
  3175. /*{{{  gprintf*/
  3176. /* extended sprintf */
  3177. void gprintf(dest, format, log_base, x)
  3178. char *dest, *format;
  3179. double log_base, x; /* we print one number in a number of different formats */
  3180. {
  3181.     char temp[MAX_LINE_LEN];
  3182.     char *t;
  3183.  
  3184.     for (;;) {
  3185.         /*{{{  copy to dest until %*/
  3186.         while (*format != '%')
  3187.             if (!(*dest++ = *format++))
  3188.                 return;  /* end of format */
  3189.         /*}}}*/
  3190.  
  3191.         /*{{{  check for %%*/
  3192.         if (format[1]=='%') {
  3193.             *dest++ = '%';
  3194.             format += 2;
  3195.             continue;
  3196.         }
  3197.         /*}}}*/
  3198.  
  3199.         /*{{{  copy format part to temp, excluding conversion character*/
  3200.         t=temp;
  3201.         *t++ = '%';
  3202.         while (isdigit(*++format) || *format=='.' || *format=='-' || *format=='+')
  3203.             *t++ = *format;
  3204.         /*}}}*/
  3205.  
  3206.         /*{{{  convert conversion character*/
  3207.         switch (*format) {
  3208.             /*{{{  x and o*/
  3209.             case 'x': case 'X':
  3210.             case 'o': case 'O':
  3211.                 t[0]=*format++;
  3212.                 t[1]=0;
  3213.                 sprintf(dest, temp, (int) x);
  3214.                 dest += strlen(dest);
  3215.                 break;
  3216.             /*}}}*/
  3217.             /*{{{  e, f and g*/
  3218.             case 'e': case 'E':
  3219.             case 'f': case 'F':
  3220.             case 'g': case 'G':
  3221.                 t[0]=*format++;
  3222.                 t[1]=0;
  3223.                 sprintf(dest, temp, x);
  3224.                 dest += strlen(dest);
  3225.                 break;
  3226.             /*}}}*/
  3227.             /*{{{  l*/
  3228.             case 'l':
  3229.                 {
  3230.                     double mantissa;
  3231.                     mant_exp(log_base, x, 0, &mantissa, NULL);
  3232.                     t[0]='f';
  3233.                     t[1]=0;
  3234.                     sprintf(dest, temp, mantissa);
  3235.                     dest += strlen(dest);
  3236.                     ++format;
  3237.                     break;
  3238.                 }
  3239.             /*}}}*/
  3240.             /*{{{  t*/
  3241.             case 't':
  3242.                 {
  3243.                     double mantissa;
  3244.                     mant_exp(1.0, x, 0, &mantissa, NULL);
  3245.                     t[0]='f';
  3246.                     t[1]=0;
  3247.                     sprintf(dest, temp, mantissa);
  3248.                     dest += strlen(dest);
  3249.                     ++format;
  3250.                     break;
  3251.                 }
  3252.             /*}}}*/
  3253.             /*{{{  s*/
  3254.             case 's':
  3255.                 {
  3256.                     double mantissa;
  3257.                     mant_exp(1.0, x, 1, &mantissa, NULL);
  3258.                     t[0]='f';
  3259.                     t[1]=0;
  3260.                     sprintf(dest, temp, mantissa);
  3261.                     dest += strlen(dest);
  3262.                     ++format;
  3263.                     break;
  3264.                 }
  3265.             /*}}}*/
  3266.             /*{{{  L*/
  3267.             case 'L':
  3268.                 {
  3269.                     int power;
  3270.                     mant_exp(log_base, x, 0, NULL, &power);
  3271.                     t[0]='d';
  3272.                     t[1]=0;
  3273.                     sprintf(dest, temp, power);
  3274.                     dest += strlen(dest);
  3275.                     ++format;
  3276.                     break;
  3277.                 }
  3278.             /*}}}*/
  3279.             /*{{{  T*/
  3280.             case 'T':
  3281.                 {
  3282.                     int power;
  3283.                     mant_exp(1.0, x, 0, NULL, &power);
  3284.                     t[0]='d';
  3285.                     t[1]=0;
  3286.                     sprintf(dest, temp, power);
  3287.                     dest += strlen(dest);
  3288.                     ++format;
  3289.                     break;
  3290.                 }
  3291.             /*}}}*/
  3292.             /*{{{  S*/
  3293.             case 'S':
  3294.                 {
  3295.                     int power;
  3296.                     mant_exp(1.0, x, 1, NULL, &power);
  3297.                     t[0]='d';
  3298.                     t[1]=0;
  3299.                     sprintf(dest, temp, power);
  3300.                     dest += strlen(dest);
  3301.                     ++format;
  3302.                     break;
  3303.                 }
  3304.             /*}}}*/
  3305.             /*{{{  c*/
  3306.             case 'c':
  3307.                 {
  3308.                     int power;
  3309.                     mant_exp(1.0, x, 0, NULL, &power);
  3310.                     t[0]='c';
  3311.                     t[1]=0;
  3312.                     power = power / 3 + 6;
  3313.                     if (power<0 || power>12) power=12;
  3314.                     /* please extend the range ! */
  3315.                     sprintf(dest, temp, "**pnum kMG***"[power]);
  3316.                     dest += strlen(dest);
  3317.                     ++format;
  3318.                     break;
  3319.                 }
  3320.             /*}}}*/
  3321.             default:
  3322.                 int_error("Bad format character", NO_CARET);
  3323.         }
  3324.         /*}}}*/
  3325.     }
  3326. }
  3327. /*}}}*/
  3328.  
  3329. /*{{{  gen_tics*/
  3330. /* uses global arrays ticstep[], ticfmt[], min_array[], max_array[],
  3331.  * auto_array[], log_array[], log_base_array[]
  3332.  * we use any of GRID_X/Y/X2/Y2 and  _MX/_MX2/etc - caller is expected
  3333.  * to clear the irrelevent fields from global grid bitmask
  3334.  * note this is also called from graph3d, so we need GRID_Z too
  3335.  */
  3336.  
  3337. void gen_tics(axis, def, grid, minitics, minifreq, callback)
  3338. int axis; /* FIRST_X_AXIS, etc */
  3339. struct ticdef *def; /* tic defn */
  3340. int grid; /* GRID_X | GRID_MX etc */
  3341. int minitics; /* minitics - off/default/auto/explicit */
  3342. double minifreq; /* frequency */
  3343. tic_callback callback;  /* fn to call to actually do the work */
  3344. {
  3345.     /* seperate main-tic part of grid */
  3346.     int lgrd= (grid&(GRID_X|GRID_Y|GRID_X2|GRID_Y2|GRID_Z)) ? grid_linetype : -2;
  3347.     int mgrd= (grid&(GRID_MX|GRID_MY|GRID_MX2|GRID_MY2|GRID_MZ)) ? mgrid_linetype : -2;
  3348.  
  3349.     if (def->type==TIC_USER) {  /* special case */
  3350.     /*{{{  do user tics then return*/
  3351.     struct ticmark *mark = def->def.user;
  3352.     double uncertain=(max_array[axis]-min_array[axis])/10;
  3353.     double ticmin=min_array[axis]-SIGNIF*uncertain;
  3354.     double internal_max=max_array[axis]+SIGNIF*uncertain;
  3355.     
  3356.     for (mark=def->def.user; mark; mark=mark->next) {
  3357.         char label[64];
  3358.         double internal = log_array[axis] ? log(mark->position)/log_base_array[axis] : mark->position;
  3359.         if (!inrange(internal, ticmin, internal_max))
  3360.             continue;
  3361.         /* polar labels always +ve */
  3362.         sprintf(label, mark->label ? mark->label : ticfmt[axis], polar ? fabs(mark->position):mark->position);
  3363.         (*callback)(axis, internal, label, lgrd);
  3364.     }
  3365.     
  3366.     return; /* NO MINITICS FOR USER-DEF TICS */
  3367.        }
  3368.     /*}}}*/
  3369.  
  3370.     /* series-tics
  3371.      * need to distinguish user co-ords from internal co-ords.
  3372.      * - for logscale, internal=log(user), else internal=user
  3373.      *
  3374.      * The minitics are a bit of a drag - we need to distinuish
  3375.      * the cases step>1 from step==1.
  3376.      * If step=1, we are looking at 1,10,100,1000 for example, so
  3377.      * minitics are 2,5,8, ...  - done in user co-ordinates
  3378.      * If step>1, we are looking at 1,1e6,1e12 for example, so
  3379.      * minitics are 10,100,1000,... - done in internal co-ords
  3380.      */
  3381.  
  3382.     {
  3383.     double tic; /* loop counter */
  3384.     double internal; /* in internal co-ords */
  3385.     double user;     /* in user co-ords */
  3386.     double start,step,end;
  3387.     double lmin=min_array[axis], lmax=max_array[axis];
  3388.     double internal_min, internal_max;    /* to allow for rounding errors */
  3389.     double ministart=0, ministep=1, miniend=1; /* internal or user - depends on step */
  3390.  
  3391.     /* gprintf uses log10() of base - log_base_array is log() */
  3392.     double log_base = log_array[axis] ? log10(base_array[axis]) : 1.0;
  3393.  
  3394.     if (lmax < lmin) {
  3395.         /* hmm - they have set reversed range for some reason */
  3396.         double temp=lmin; lmin=lmax; lmax=temp;
  3397.     }
  3398.     
  3399.     /*{{{  choose start, step and end*/
  3400.     switch (def->type) {
  3401.         case TIC_SERIES:
  3402.             if (log_array[axis]) {
  3403.                 /* we can tolerate start<=0 if step and end > 0 */
  3404.                 if (def->def.series.end <= 0 ||
  3405.                     def->def.series.incr <= 0)
  3406.                     return; /* just quietly ignore */
  3407.                 step=log(def->def.series.incr)/log_base_array[axis];
  3408.                 end=log(def->def.series.end)/log_base_array[axis];
  3409.                 start=def->def.series.start > 0 ?
  3410.                       log(def->def.series.start) / log_base_array[axis] :
  3411.                         step;
  3412.             } else {
  3413.                 start=def->def.series.start;
  3414.                 step=def->def.series.incr;
  3415.                 end=def->def.series.end;
  3416.                 if (start==-VERYLARGE)
  3417.                     start=step*floor(lmin/step);
  3418.                 if (end == VERYLARGE)
  3419.                     end=step*ceil(lmax/step);
  3420.             }
  3421.             break;
  3422.         case TIC_COMPUTED:
  3423.             /* round to multiple of step */
  3424.             start=ticstep[axis]*floor(lmin/ticstep[axis]);
  3425.             step=ticstep[axis];
  3426.             end=ticstep[axis]*ceil(lmax/ticstep[axis]);
  3427.             break;
  3428.         case TIC_MONTH:
  3429.             start=floor(lmin);
  3430.             end=ceil(lmax);
  3431.             step=floor((end-start)/12);
  3432.             if (step<1) step=1;
  3433.             break;
  3434.         case TIC_DAY:
  3435.             start=floor(lmin);
  3436.             end=ceil(lmax);
  3437.             step=floor((end-start)/14);
  3438.             if (step<1) step=1;
  3439.             break;
  3440.         default:
  3441.             graph_error("Internal error : unknown tic type");
  3442.             return; /* avoid gcc -Wall warning about start */
  3443.     }
  3444.     /*}}}*/
  3445.  
  3446.     /*{{{  ensure ascending order*/
  3447.     if (end < start) {
  3448.         double temp;
  3449.         temp=end; end=start; start=temp;
  3450.     }
  3451.     step=fabs(step);
  3452.     /*}}}*/
  3453.  
  3454.     if (minitics) {
  3455.         /*{{{  figure out ministart, ministep, miniend*/
  3456.         if (minitics==MINI_USER) {
  3457.             /* they have said what they want */
  3458.             if (minifreq<=0)
  3459.                 minitics=0;  /* not much else we can do */
  3460.             else {
  3461.                 ministart = ministep=step/minifreq;
  3462.                 miniend = step;
  3463.             }
  3464.         } else if (log_array[axis]) {
  3465.             if (step>1.5) { /* beware rounding errors */
  3466.                 /*{{{  10,100,1000 case*/
  3467.                 /* no more than five minitics */
  3468.                 ministart = ministep = (int) (0.2*step);
  3469.                 if (ministep < 1)
  3470.                     ministart = ministep = 1;
  3471.                 miniend = step;
  3472.                 /*}}}*/
  3473.             } else {
  3474.                 /*{{{  2,5,8 case*/
  3475.                 miniend = base_array[axis];
  3476.                 if (end-start >= 10)
  3477.                     minitics=0; /* none */
  3478.                 else if (end-start >= 5) {
  3479.                     ministart=2;
  3480.                     ministep=3;
  3481.                 } else {
  3482.                     ministart=2;
  3483.                     ministep=1;
  3484.                 }
  3485.                 /*}}}*/
  3486.             }
  3487.         } else if (datatype[axis]==TIME) {
  3488.             ministart=ministep=make_ltic(timelevel[axis], step);
  3489.             miniend=step*0.9;
  3490.         } else if (minitics==MINI_AUTO) {
  3491.             ministart=ministep=0.1*step;
  3492.             miniend=step;
  3493.         } else
  3494.             minitics=0;
  3495.         
  3496.         if (ministep<=0)
  3497.             minitics=0; /* dont get stuck in infinite loop */
  3498.         /*}}}*/
  3499.     }
  3500.     
  3501.     /*{{{  a few tweaks and checks*/
  3502.     /* watch rounding errors */
  3503.     end += SIGNIF*step;
  3504.     internal_max=lmax + step*SIGNIF;
  3505.     internal_min=lmin - step*SIGNIF;
  3506.     
  3507.     if (step==0)
  3508.         return;  /* just quietly ignore them ! */
  3509.     /*}}}*/
  3510.     
  3511.     for (tic=start; tic<=end; tic+=step) {
  3512.         /*{{{  calc internal and user co-ords*/
  3513.         if (!log_array[axis]) {
  3514.             internal = datatype[axis]==TIME ? time_tic_just(timelevel[axis],tic): tic;
  3515.             user=CheckZero(internal,step);
  3516.         } else {
  3517.             /* log scale => dont need to worry about zero ? */
  3518.             internal=tic;
  3519.             user=pow(base_array[axis], internal);
  3520.         }
  3521.         /*}}}*/
  3522.         if (internal > internal_max)
  3523.             break; /* gone too far - end of series = VERYLARGE perhaps */
  3524.         if (internal >= internal_min) {
  3525.             /* continue; */ /* maybe minitics!!!. user series starts below min ? */
  3526.  
  3527.             /*{{{  draw tick via callback*/
  3528.             switch(def->type) {
  3529.                 case TIC_DAY : {
  3530.                     int d = (long)floor(user+0.5) % 7;
  3531.                     if (d<0) d+=7;
  3532.                     (*callback)(axis,internal, abbrev_day_names[d], lgrd);
  3533.                     break;
  3534.                 }
  3535.                 case TIC_MONTH: {
  3536.                     int m = (long)floor(user+0.5) % 12;
  3537.                     if (m<0) m+=12;
  3538.                     (*callback)(axis, internal, abbrev_month_names[m], lgrd);
  3539.                     break;
  3540.                 }
  3541.                 default: { /* comp or series */
  3542.                     char label[64];
  3543.                     if ( datatype[axis] == TIME ) {
  3544.                         /* If they are doing polar time plot, good luck to them */
  3545.                         gstrftime(label,24,ticfmt[axis],(double)user);
  3546.                     } else {
  3547.                         gprintf(label, ticfmt[axis], log_base, polar?fabs(user):user);
  3548.                     }
  3549.                     (*callback)(axis, internal, label, lgrd);
  3550.                 }
  3551.             }
  3552.             /*}}}*/
  3553.  
  3554.         }
  3555.         if (minitics) {
  3556.             /*{{{  process minitics*/
  3557.             double mplace, mtic;
  3558.             for (mplace=ministart; mplace < miniend; mplace += ministep) {
  3559.                 if ( datatype[axis] == TIME )
  3560.                     mtic = time_tic_just(timelevel[axis]-1,internal+mplace);
  3561.                 else
  3562.                     mtic=internal + (log_array[axis]&&step<=1.5 ? log(mplace)/log_base_array[axis] : mplace);
  3563.                 if (inrange(mtic, internal_min, internal_max))
  3564.                      callback(axis, mtic, NULL, mgrd);
  3565.             }
  3566.             /*}}}*/
  3567.         } 
  3568.     }
  3569.     }
  3570. }
  3571. /*}}}*/
  3572.  
  3573. /*{{{  map_position*/
  3574. static void map_position(pos, x, y, what)
  3575. struct position *pos;
  3576. unsigned int *x, *y;
  3577. char *what;
  3578. {
  3579.     switch (pos->scalex) {
  3580.         case first_axes:
  3581.             {
  3582.                 double xx=LogScale(pos->x, log_array[FIRST_X_AXIS], log_base_array[FIRST_X_AXIS], what, "x");
  3583.                 *x=xleft + (xx-min_array[FIRST_X_AXIS])*scale[FIRST_X_AXIS]+0.5;
  3584.                 break;
  3585.             }
  3586.         case second_axes:
  3587.             {
  3588.                 double xx=LogScale(pos->x, log_array[SECOND_X_AXIS], log_base_array[SECOND_X_AXIS], what, "x");
  3589.                 *x=xleft + (xx-min_array[SECOND_X_AXIS])*scale[SECOND_X_AXIS]+0.5;
  3590.                 break;
  3591.             }
  3592.         case graph:
  3593.             {
  3594.                 *x=xleft + pos->x*(xright-xleft) + 0.5;
  3595.                 break;
  3596.             }
  3597.         case screen:
  3598.             {
  3599.                 register struct termentry *t = term;
  3600.                 *x = pos->x*(t->xmax) + 0.5;
  3601.                 break;
  3602.             }
  3603.     }
  3604.     switch (pos->scaley) {
  3605.         case first_axes:
  3606.             {
  3607.                 double yy=LogScale(pos->y, log_array[FIRST_Y_AXIS], log_base_array[FIRST_Y_AXIS], what, "y");
  3608.                 *y=ybot  + (yy-min_array[FIRST_Y_AXIS])*scale[FIRST_Y_AXIS]+0.5;
  3609.                 return;
  3610.             }
  3611.         case second_axes:
  3612.             {
  3613.                 double yy=LogScale(pos->y, log_array[SECOND_Y_AXIS], log_base_array[SECOND_Y_AXIS], what, "y");
  3614.                 *y=ybot  + (yy-min_array[SECOND_Y_AXIS])*scale[SECOND_Y_AXIS]+0.5;
  3615.                 return;
  3616.             }
  3617.         case graph:
  3618.             {
  3619.                 *y=ybot  + pos->y*(ytop-ybot) + 0.5;
  3620.                 return;
  3621.             }
  3622.         case screen:
  3623.             {
  3624.                 register struct termentry *t = term;
  3625.                 *y = pos->y*(t->ymax) + 0.5;
  3626.                 return;                
  3627.             }
  3628.     }
  3629. }
  3630. /*}}}*/
  3631.